summaryrefslogtreecommitdiffstats
path: root/bridges/source/jni_uno/jni_java2uno.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bridges/source/jni_uno/jni_java2uno.cxx')
-rw-r--r--bridges/source/jni_uno/jni_java2uno.cxx624
1 files changed, 624 insertions, 0 deletions
diff --git a/bridges/source/jni_uno/jni_java2uno.cxx b/bridges/source/jni_uno/jni_java2uno.cxx
new file mode 100644
index 000000000..b6c4c6ea9
--- /dev/null
+++ b/bridges/source/jni_uno/jni_java2uno.cxx
@@ -0,0 +1,624 @@
+/* -*- 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 <algorithm>
+#include <cassert>
+
+#include <sal/alloca.h>
+
+#include "jni_bridge.h"
+#include "jniunoenvironmentdata.hxx"
+
+namespace jni_uno
+{
+
+
+jobject Bridge::map_to_java(
+ JNI_context const & jni,
+ uno_Interface * pUnoI, JNI_interface_type_info const * info ) const
+{
+ // get oid
+ rtl_uString * pOid = nullptr;
+ (*m_uno_env->getObjectIdentifier)( m_uno_env, &pOid, pUnoI );
+ assert( pOid != nullptr );
+ OUString oid( pOid, SAL_NO_ACQUIRE );
+
+ // opt getRegisteredInterface()
+ JLocalAutoRef jo_oid( jni, ustring_to_jstring( jni, oid.pData ) );
+ jvalue args[ 2 ];
+ args[ 0 ].l = jo_oid.get();
+ args[ 1 ].l = info->m_type;
+ jobject jo_iface = jni->CallObjectMethodA(
+ getJniInfo()->m_object_java_env,
+ getJniInfo()->m_method_IEnvironment_getRegisteredInterface, args );
+ jni.ensure_no_exception();
+
+ if (jo_iface == nullptr) // no registered iface
+ {
+ // register uno interface
+ (*m_uno_env->registerInterface)(
+ m_uno_env, reinterpret_cast< void ** >( &pUnoI ),
+ oid.pData, reinterpret_cast<typelib_InterfaceTypeDescription *>(info->m_td.get()) );
+
+ // create java and register java proxy
+ jvalue args2[ 8 ];
+ acquire();
+ args2[ 0 ].j = reinterpret_cast< sal_Int64 >( this );
+ (*pUnoI->acquire)( pUnoI );
+ args2[ 1 ].l = getJniInfo()->m_object_java_env;
+ args2[ 2 ].j = reinterpret_cast< sal_Int64 >( pUnoI );
+ typelib_typedescription_acquire( info->m_td.get() );
+ args2[ 3 ].j = reinterpret_cast< sal_Int64 >( info->m_td.get() );
+ args2[ 4 ].l = info->m_type;
+ args2[ 5 ].l = jo_oid.get();
+ args2[ 6 ].l = info->m_proxy_ctor;
+ auto * envData = static_cast<jni_uno::JniUnoEnvironmentData *>(
+ m_java_env->pContext);
+ {
+ osl::MutexGuard g(envData->mutex);
+ args2[ 7 ].l = envData->asynchronousFinalizer;
+ }
+ jo_iface = jni->CallStaticObjectMethodA(
+ getJniInfo()->m_class_JNI_proxy,
+ getJniInfo()->m_method_JNI_proxy_create, args2 );
+ jni.ensure_no_exception();
+ }
+
+ assert( jo_iface != nullptr );
+ return jo_iface;
+}
+
+
+void Bridge::handle_uno_exc( JNI_context const & jni, uno_Any * uno_exc ) const
+{
+ if (uno_exc->pType->eTypeClass == typelib_TypeClass_EXCEPTION)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ // append java stack trace to Message member
+ static_cast< ::com::sun::star::uno::Exception * >(
+ uno_exc->pData )->Message += jni.get_stack_trace();
+#endif
+ SAL_INFO(
+ "bridges",
+ "exception occurred java->uno: ["
+ << OUString::unacquired(&uno_exc->pType->pTypeName) << "] "
+ << static_cast<css::uno::Exception const *>(
+ uno_exc->pData)->Message);
+ // signal exception
+ jvalue java_exc;
+ try
+ {
+ map_to_java(
+ jni, &java_exc, uno_exc->pData, uno_exc->pType, nullptr,
+ true /* in */, false /* no out */ );
+ }
+ catch (...)
+ {
+ uno_any_destruct( uno_exc, nullptr );
+ throw;
+ }
+ uno_any_destruct( uno_exc, nullptr );
+
+ JLocalAutoRef jo_exc( jni, java_exc.l );
+ jint res = jni->Throw( static_cast<jthrowable>(jo_exc.get()) );
+ if (res != 0)
+ {
+ // call toString()
+ JLocalAutoRef jo_descr(
+ jni, jni->CallObjectMethodA(
+ jo_exc.get(), getJniInfo()->m_method_Object_toString, nullptr ) );
+ jni.ensure_no_exception();
+ throw BridgeRuntimeError(
+ "throwing java exception failed: "
+ + jstring_to_oustring( jni, static_cast<jstring>(jo_descr.get()) )
+ + jni.get_stack_trace() );
+ }
+ }
+ else
+ {
+ OUString message(
+ "thrown exception is no uno exception: " +
+ OUString::unacquired( &uno_exc->pType->pTypeName ) +
+ jni.get_stack_trace() );
+ uno_any_destruct( uno_exc, nullptr );
+ throw BridgeRuntimeError( message );
+ }
+}
+
+namespace {
+
+union largest
+{
+ sal_Int64 n;
+ double d;
+ void * p;
+ uno_Any a;
+};
+
+}
+
+jobject Bridge::call_uno(
+ JNI_context const & jni,
+ uno_Interface * pUnoI, typelib_TypeDescription * member_td,
+ typelib_TypeDescriptionReference * return_type,
+ sal_Int32 nParams, typelib_MethodParameter const * pParams,
+ jobjectArray jo_args /* may be 0 */ ) const
+{
+ // return mem
+ sal_Int32 return_size;
+ switch (return_type->eTypeClass) {
+ case typelib_TypeClass_VOID:
+ return_size = 0;
+ break;
+
+ case typelib_TypeClass_STRUCT:
+ case typelib_TypeClass_EXCEPTION:
+ return_size = std::max(
+ TypeDescr(return_type).get()->nSize,
+ static_cast< sal_Int32 >(sizeof (largest)));
+ break;
+
+ default:
+ return_size = sizeof (largest);
+ break;
+ }
+
+ char * mem = static_cast<char *>(alloca(
+ (nParams * sizeof (void *)) +
+ return_size + (nParams * sizeof (largest)) ));
+ void ** uno_args = reinterpret_cast<void **>(mem);
+ void * uno_ret = return_size == 0 ? nullptr : (mem + (nParams * sizeof (void *)));
+ largest * uno_args_mem = reinterpret_cast<largest *>
+ (mem + (nParams * sizeof (void *)) + return_size);
+
+ assert( (nParams == 0) || (nParams == jni->GetArrayLength( jo_args )) );
+ for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos )
+ {
+ typelib_MethodParameter const & param = pParams[ nPos ];
+ typelib_TypeDescriptionReference * type = param.pTypeRef;
+
+ uno_args[ nPos ] = &uno_args_mem[ nPos ];
+ if (type->eTypeClass == typelib_TypeClass_STRUCT ||
+ type->eTypeClass == typelib_TypeClass_EXCEPTION)
+ {
+ TypeDescr td( type );
+ if (sal::static_int_cast< sal_uInt32 >(td.get()->nSize)
+ > sizeof (largest))
+ uno_args[ nPos ] = alloca( td.get()->nSize );
+ }
+
+ if (param.bIn)
+ {
+ try
+ {
+ JLocalAutoRef jo_arg(
+ jni, jni->GetObjectArrayElement( jo_args, nPos ) );
+ jni.ensure_no_exception();
+ jvalue java_arg;
+ java_arg.l = jo_arg.get();
+ map_to_uno(
+ jni, uno_args[ nPos ], java_arg, type, nullptr,
+ false /* no assign */, param.bOut,
+ true /* special wrapped integral types */ );
+ }
+ catch (...)
+ {
+ // cleanup uno in args
+ for ( sal_Int32 n = 0; n < nPos; ++n )
+ {
+ typelib_MethodParameter const & p = pParams[ n ];
+ if (p.bIn)
+ {
+ uno_type_destructData(
+ uno_args[ n ], p.pTypeRef, nullptr );
+ }
+ }
+ throw;
+ }
+ }
+ }
+
+ uno_Any uno_exc_holder;
+ uno_Any * uno_exc = &uno_exc_holder;
+ // call binary uno
+ (*pUnoI->pDispatcher)( pUnoI, member_td, uno_ret, uno_args, &uno_exc );
+
+ if (uno_exc == nullptr)
+ {
+ // convert out args; destruct uno args
+ for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos )
+ {
+ typelib_MethodParameter const & param = pParams[ nPos ];
+ typelib_TypeDescriptionReference * type = param.pTypeRef;
+ if (param.bOut)
+ {
+ try
+ {
+ // get out holder array[ 1 ]
+ JLocalAutoRef jo_out_holder(
+ jni, jni->GetObjectArrayElement( jo_args, nPos ) );
+ jni.ensure_no_exception();
+ jvalue java_arg;
+ java_arg.l = jo_out_holder.get();
+ map_to_java(
+ jni, &java_arg, uno_args[ nPos ], type, nullptr,
+ true /* in */, true /* out holder */ );
+ }
+ catch (...)
+ {
+ // cleanup further uno args
+ for ( sal_Int32 n = nPos; n < nParams; ++n )
+ {
+ uno_type_destructData(
+ uno_args[ n ], pParams[ n ].pTypeRef, nullptr );
+ }
+ // cleanup uno return value
+ uno_type_destructData( uno_ret, return_type, nullptr );
+ throw;
+ }
+ }
+ if (typelib_TypeClass_DOUBLE < type->eTypeClass &&
+ type->eTypeClass != typelib_TypeClass_ENUM) // opt
+ {
+ uno_type_destructData( uno_args[ nPos ], type, nullptr );
+ }
+ }
+
+ if (return_type->eTypeClass != typelib_TypeClass_VOID)
+ {
+ // convert uno return value
+ jvalue java_ret;
+ try
+ {
+ map_to_java(
+ jni, &java_ret, uno_ret, return_type, nullptr,
+ true /* in */, false /* no out */,
+ true /* special_wrapped_integral_types */ );
+ }
+ catch (...)
+ {
+ uno_type_destructData( uno_ret, return_type, nullptr );
+ throw;
+ }
+ if (typelib_TypeClass_DOUBLE < return_type->eTypeClass &&
+ return_type->eTypeClass != typelib_TypeClass_ENUM) // opt
+ {
+ uno_type_destructData( uno_ret, return_type, nullptr );
+ }
+ return java_ret.l;
+ }
+ return nullptr; // void return
+ }
+ else // exception occurred
+ {
+ // destruct uno in args
+ for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos )
+ {
+ typelib_MethodParameter const & param = pParams[ nPos ];
+ if (param.bIn)
+ uno_type_destructData( uno_args[ nPos ], param.pTypeRef, nullptr );
+ }
+
+ handle_uno_exc( jni, uno_exc );
+ return nullptr;
+ }
+}
+
+}
+
+using namespace ::jni_uno;
+
+extern "C"
+{
+
+
+SAL_JNI_EXPORT jobject
+JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_dispatch_1call(
+ JNIEnv * jni_env, jobject jo_proxy, jlong bridge_handle, jstring jo_method,
+ jobjectArray jo_args /* may be 0 */ )
+{
+ Bridge const * bridge = reinterpret_cast< Bridge const * >( bridge_handle );
+ JNI_info const * jni_info = bridge->getJniInfo();
+ JNI_context jni(
+ jni_info, jni_env,
+ static_cast< jobject >(
+ static_cast<JniUnoEnvironmentData *>(bridge->m_java_env->pContext)
+ ->machine->getClassLoader()));
+
+ OUString method_name;
+
+ try
+ {
+ method_name = jstring_to_oustring( jni, jo_method );
+ SAL_INFO(
+ "bridges",
+ "java->uno call: " << method_name << " on oid "
+ << jstring_to_oustring(
+ jni,
+ static_cast<jstring>(
+ JLocalAutoRef(
+ jni,
+ jni->GetObjectField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_oid))
+ .get())));
+ // special IQueryInterface.queryInterface()
+ if ( method_name == "queryInterface" )
+ {
+ // oid
+ JLocalAutoRef jo_oid(
+ jni, jni->GetObjectField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_oid ) );
+ // type
+ JLocalAutoRef jo_type(
+ jni, jni->GetObjectArrayElement( jo_args, 0 ) );
+ jni.ensure_no_exception();
+
+ JLocalAutoRef jo_type_name(
+ jni, jni->GetObjectField(
+ jo_type.get(), jni_info->m_field_Type_typeName ) );
+ if (! jo_type_name.is())
+ {
+ throw BridgeRuntimeError(
+ "incomplete type object: no type name!" +
+ jni.get_stack_trace() );
+ }
+ OUString type_name(
+ jstring_to_oustring( jni, static_cast<jstring>(jo_type_name.get()) ) );
+ JNI_type_info const * info =
+ jni_info->get_type_info( jni, type_name );
+ if (info->m_td.get()->eTypeClass != typelib_TypeClass_INTERFACE)
+ {
+ throw BridgeRuntimeError(
+ "queryInterface() call demands an INTERFACE type!" );
+ }
+ JNI_interface_type_info const * iface_info =
+ static_cast< JNI_interface_type_info const * >( info );
+
+ // getRegisteredInterface() already tested in JNI_proxy:
+ // perform queryInterface call on binary uno interface
+ uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >(
+ jni->GetLongField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_receiver_handle ) );
+
+ uno_Any uno_ret;
+ void * uno_args[] = { &iface_info->m_td.get()->pWeakRef };
+ uno_Any uno_exc_holder;
+ uno_Any * uno_exc = &uno_exc_holder;
+ // call binary uno
+ (*pUnoI->pDispatcher)(
+ pUnoI, jni_info->m_XInterface_queryInterface_td.get(),
+ &uno_ret, uno_args, &uno_exc );
+ if (uno_exc == nullptr)
+ {
+ jobject jo_ret = nullptr;
+ if (uno_ret.pType->eTypeClass == typelib_TypeClass_INTERFACE)
+ {
+ uno_Interface * pUnoRet =
+ static_cast<uno_Interface *>(uno_ret.pReserved);
+ if (pUnoRet != nullptr)
+ {
+ try
+ {
+ jo_ret =
+ bridge->map_to_java( jni, pUnoRet, iface_info );
+ }
+ catch (...)
+ {
+ uno_any_destruct( &uno_ret, nullptr );
+ throw;
+ }
+ }
+ }
+ uno_any_destruct( &uno_ret, nullptr );
+ return jo_ret;
+ }
+ else
+ {
+ bridge->handle_uno_exc( jni, uno_exc );
+ return nullptr;
+ }
+ }
+
+ typelib_InterfaceTypeDescription * td =
+ reinterpret_cast< typelib_InterfaceTypeDescription * >(
+ jni->GetLongField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_td_handle ) );
+ uno_Interface * pUnoI =
+ reinterpret_cast< uno_Interface * >(
+ jni->GetLongField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_receiver_handle ) );
+
+ typelib_TypeDescriptionReference ** ppAllMembers = td->ppAllMembers;
+ for ( sal_Int32 nPos = td->nAllMembers; nPos--; )
+ {
+ // try to avoid getting typedescription as long as possible,
+ // because of a Mutex.acquire() in
+ // typelib_typedescriptionreference_getDescription()
+ typelib_TypeDescriptionReference * member_type =
+ ppAllMembers[ nPos ];
+
+ // check method_name against fully qualified type_name
+ // of member_type; type_name is of the form
+ // <name> "::" <method_name> *(":@" <idx> "," <idx> ":" <name>)
+ OUString const & type_name =
+ OUString::unacquired( &member_type->pTypeName );
+ sal_Int32 offset = type_name.indexOf( ':' ) + 2;
+ assert(offset >= 2);
+ assert(offset < type_name.getLength());
+ assert(type_name[offset - 1] == ':' );
+ sal_Int32 remainder = type_name.getLength() - offset;
+ if (member_type->eTypeClass == typelib_TypeClass_INTERFACE_METHOD)
+ {
+ if ((method_name.getLength() == remainder
+ || (method_name.getLength() < remainder
+ && type_name[offset + method_name.getLength()] == ':'))
+ && type_name.match(method_name, offset))
+ {
+ TypeDescr member_td( member_type );
+ typelib_InterfaceMethodTypeDescription * method_td =
+ reinterpret_cast<
+ typelib_InterfaceMethodTypeDescription * >(
+ member_td.get() );
+ return bridge->call_uno(
+ jni, pUnoI, member_td.get(),
+ method_td->pReturnTypeRef,
+ method_td->nParams, method_td->pParams,
+ jo_args );
+ }
+ }
+ else // attribute
+ {
+ assert(
+ member_type->eTypeClass ==
+ typelib_TypeClass_INTERFACE_ATTRIBUTE );
+
+ if (method_name.getLength() >= 3
+ && (method_name.getLength() - 3 == remainder
+ || (method_name.getLength() - 3 < remainder
+ && type_name[
+ offset + (method_name.getLength() - 3)] == ':'))
+ && method_name[1] == 'e' && method_name[2] == 't'
+ && rtl_ustr_compare_WithLength(
+ type_name.getStr() + offset,
+ method_name.getLength() - 3,
+ method_name.getStr() + 3,
+ method_name.getLength() - 3) == 0)
+ {
+ if (method_name[ 0 ] == 'g')
+ {
+ TypeDescr member_td( member_type );
+ typelib_InterfaceAttributeTypeDescription * attr_td =
+ reinterpret_cast<
+ typelib_InterfaceAttributeTypeDescription * >(
+ member_td.get() );
+ return bridge->call_uno(
+ jni, pUnoI, member_td.get(),
+ attr_td->pAttributeTypeRef,
+ 0, nullptr,
+ jo_args );
+ }
+ else if (method_name[ 0 ] == 's')
+ {
+ TypeDescr member_td( member_type );
+ typelib_InterfaceAttributeTypeDescription * attr_td =
+ reinterpret_cast<
+ typelib_InterfaceAttributeTypeDescription * >(
+ member_td.get() );
+ if (! attr_td->bReadOnly)
+ {
+ typelib_MethodParameter param;
+ param.pTypeRef = attr_td->pAttributeTypeRef;
+ param.bIn = true;
+ param.bOut = false;
+ return bridge->call_uno(
+ jni, pUnoI, member_td.get(),
+ jni_info->m_void_type.getTypeLibType(),
+ 1, &param,
+ jo_args );
+ }
+ }
+ }
+ }
+ }
+ // the thing that should not be... no method info found!
+ throw BridgeRuntimeError(
+ "calling undeclared function on interface "
+ + OUString::unacquired(&td->aBase.pTypeName)
+ + ": " + method_name + jni.get_stack_trace() );
+ }
+ catch (const BridgeRuntimeError & err)
+ {
+ SAL_WARN(
+ "bridges",
+ "Java calling UNO method " << method_name << ": " << err.m_message);
+ // notify RuntimeException
+ OString cstr_msg(
+ "[jni_uno bridge error] Java calling UNO method "
+ + OUStringToOString(method_name, RTL_TEXTENCODING_JAVA_UTF8) + ": "
+ + OUStringToOString(err.m_message, RTL_TEXTENCODING_JAVA_UTF8));
+ if (jni->ThrowNew(jni_info->m_class_RuntimeException, cstr_msg.getStr())
+ != 0)
+ {
+ assert( false );
+ }
+ return nullptr;
+ }
+ catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
+ {
+ SAL_WARN("bridges", "attaching current thread to java failed");
+ OString cstr_msg(
+ "[jni_uno bridge error] attaching current thread to java failed"
+ + OUStringToOString(
+ jni.get_stack_trace(), RTL_TEXTENCODING_JAVA_UTF8));
+ if (jni->ThrowNew(jni_info->m_class_RuntimeException, cstr_msg.getStr())
+ != 0)
+ {
+ assert( false );
+ }
+ return nullptr;
+ }
+}
+
+
+SAL_JNI_EXPORT void
+JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_finalize__J(
+ JNIEnv * jni_env, jobject jo_proxy, jlong bridge_handle )
+{
+ Bridge const * bridge = reinterpret_cast< Bridge const * >( bridge_handle );
+ JNI_info const * jni_info = bridge->getJniInfo();
+ JNI_context jni(
+ jni_info, jni_env,
+ static_cast< jobject >(
+ static_cast<JniUnoEnvironmentData *>(bridge->m_java_env->pContext)
+ ->machine->getClassLoader()));
+
+ uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >(
+ jni->GetLongField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_receiver_handle ) );
+ typelib_TypeDescription * td =
+ reinterpret_cast< typelib_TypeDescription * >(
+ jni->GetLongField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_td_handle ) );
+ SAL_INFO(
+ "bridges",
+ "freeing java uno proxy: "
+ << jstring_to_oustring(
+ jni,
+ static_cast<jstring>(
+ JLocalAutoRef(
+ jni,
+ jni->GetObjectField(
+ jo_proxy, jni_info->m_field_JNI_proxy_m_oid))
+ .get())));
+ // revoke from uno env; has already been revoked from java env
+ (*bridge->m_uno_env->revokeInterface)( bridge->m_uno_env, pUnoI );
+ // release receiver
+ (*pUnoI->release)( pUnoI );
+ // release typedescription handle
+ typelib_typedescription_release( td );
+ // release bridge handle
+ bridge->release();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */