diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /bridges/source/jni_uno | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_info_holder.java | 61 | ||||
-rw-r--r-- | bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java | 225 | ||||
-rw-r--r-- | bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/manifest | 1 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_base.h | 260 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_bridge.cxx | 578 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_bridge.h | 121 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_data.cxx | 2476 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_helper.h | 153 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_info.cxx | 992 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_info.h | 369 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_java2uno.cxx | 624 | ||||
-rw-r--r-- | bridges/source/jni_uno/jni_uno2java.cxx | 806 | ||||
-rw-r--r-- | bridges/source/jni_uno/jniunoenvironmentdata.hxx | 57 | ||||
-rw-r--r-- | bridges/source/jni_uno/nativethreadpool.cxx | 226 |
14 files changed, 6949 insertions, 0 deletions
diff --git a/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_info_holder.java b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_info_holder.java new file mode 100644 index 000000000..e129f1032 --- /dev/null +++ b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_info_holder.java @@ -0,0 +1,61 @@ +// -*- Mode: Java; 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 . + */ + +package com.sun.star.bridges.jni_uno; + +import com.sun.star.lib.util.NativeLibraryLoader; + +public final class JNI_info_holder +{ + static { + if ("The Android Project".equals(System.getProperty("java.vendor"))) { + // See corresponding code in + // javaunohelper/com/sun/star/comp/helper/Bootstrap.java for more + // comments. + + boolean disable_dynloading = false; + try { + System.loadLibrary("lo-bootstrap"); + } catch (UnsatisfiedLinkError e) { + disable_dynloading = true; + } + + if (!disable_dynloading) + NativeLibraryLoader.loadLibrary(JNI_info_holder.class.getClassLoader(), + "java_uno"); + } else + NativeLibraryLoader.loadLibrary(JNI_info_holder.class.getClassLoader(), + "java_uno"); + } + + private static JNI_info_holder s_holder = new JNI_info_holder(); + + private static long s_jni_info_handle; + + private native void finalize( long jni_info_handle ); + + @Override + protected void finalize() throws Throwable + { + finalize( s_jni_info_handle ); + super.finalize(); + } +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java new file mode 100644 index 000000000..12817e57e --- /dev/null +++ b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java @@ -0,0 +1,225 @@ +// -*- Mode: Java; 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 . + */ + +package com.sun.star.bridges.jni_uno; + +import com.sun.star.lib.util.AsynchronousFinalizer; +import com.sun.star.lib.util.NativeLibraryLoader; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.IEnvironment; +import com.sun.star.uno.IQueryInterface; + + + +public final class JNI_proxy implements java.lang.reflect.InvocationHandler +{ + static { + if ("The Android Project".equals(System.getProperty("java.vendor"))) { + // See corresponding code in + // javaunohelper/com/sun/star/comp/helper/Bootstrap.java for more + // comments. + + boolean disable_dynloading = false; + try { + System.loadLibrary("lo-bootstrap"); + } catch (UnsatisfiedLinkError e) { + disable_dynloading = true; + } + + if (!disable_dynloading) + NativeLibraryLoader.loadLibrary(JNI_info_holder.class.getClassLoader(), + "java_uno"); + } else + NativeLibraryLoader.loadLibrary(JNI_proxy.class.getClassLoader(), + "java_uno"); + } + private static ClassLoader s_classloader = + JNI_proxy.class.getClassLoader(); + private static Class s_InvocationHandler [] = + new Class [] { java.lang.reflect.InvocationHandler.class }; + + private long m_bridge_handle; + private final IEnvironment m_java_env; + /** these 2 fields are accessed directly from C++ */ + private long m_receiver_handle; // on the C++ side, this is a "UNO_Interface *" + private long m_td_handle; // on the C++ side, this is a "typelib_TypeDescription *" + private final Type m_type; + private final String m_oid; + private final Class m_class; + private final AsynchronousFinalizer m_finalizer; + + public static String get_stack_trace( Throwable throwable ) + throws Throwable + { + boolean current_trace = false; + if (null == throwable) + { + throwable = new Throwable(); + current_trace = true; + } + java.io.StringWriter string_writer = + new java.io.StringWriter(); + java.io.PrintWriter print_writer = + new java.io.PrintWriter( string_writer, true ); + throwable.printStackTrace( print_writer ); + print_writer.flush(); + print_writer.close(); + string_writer.flush(); + String trace = string_writer.toString(); + if (current_trace) + { + // cut out first two lines + int n = trace.indexOf( '\n' ); + n = trace.indexOf( '\n', n +1 ); + trace = trace.substring( n +1 ); + } + return "\njava stack trace:\n" + trace; + } + + private native void finalize( long bridge_handle ); + + @Override + protected void finalize() + { + if (m_finalizer != null) { + m_finalizer.add(new AsynchronousFinalizer.Job() { + public void run() throws Throwable { + JNI_proxy.this.finalize( m_bridge_handle ); + } + }); + } + } + + private JNI_proxy( + long bridge_handle, IEnvironment java_env, + long receiver_handle, long td_handle, Type type, String oid, + AsynchronousFinalizer finalizer) + { + m_bridge_handle = bridge_handle; + m_java_env = java_env; + m_receiver_handle = receiver_handle; + m_td_handle = td_handle; + m_type = type; + m_oid = oid; + m_class = m_type.getZClass(); + m_finalizer = finalizer; + } + + public static Object create( + long bridge_handle, IEnvironment java_env, + long receiver_handle, long td_handle, Type type, String oid, + java.lang.reflect.Constructor proxy_ctor, + AsynchronousFinalizer finalizer) + throws Throwable + { + JNI_proxy handler = new JNI_proxy( + bridge_handle, java_env, receiver_handle, td_handle, type, oid, + finalizer); + Object proxy = proxy_ctor.newInstance( new Object [] { handler } ); + return java_env.registerInterface( proxy, new String [] { oid }, type ); + } + + public static java.lang.reflect.Constructor get_proxy_ctor( Class clazz ) + throws Throwable + { + Class proxy_class = java.lang.reflect.Proxy.getProxyClass( + s_classloader, + new Class [] { clazz, IQueryInterface.class, + com.sun.star.lib.uno.Proxy.class } ); + return proxy_class.getConstructor( s_InvocationHandler ); + } + + private native Object dispatch_call( + long bridge_handle, String method, Object args [] ) + throws Throwable; + + // InvocationHandler impl + + public Object invoke( + Object proxy, java.lang.reflect.Method method, Object args [] ) + throws Throwable + { + Class<?> decl_class = method.getDeclaringClass(); + String method_name = method.getName(); + + if (Object.class.equals( decl_class )) + { + if (method_name.equals( "hashCode" )) + { + // int hashCode() + return Integer.valueOf( m_oid.hashCode() ); + } + else if (method_name.equals( "equals" )) + { + // boolean equals( Object obj ) + return isSame(args[0]); + } + else if (method_name.equals( "toString" )) + { + // String toString() + return this.toString() + " [oid=" + m_oid + + ", type=" + m_type.getTypeName() + "]"; + } + } + // UNO interface call + else if (decl_class.isAssignableFrom( m_class )) + { + // dispatch interface call + return dispatch_call( m_bridge_handle, method_name, args ); + } + // IQueryInterface impl + else if (IQueryInterface.class.equals( decl_class )) + { + if (method_name.equals( "queryInterface" )) + { + Object registered_proxy = + m_java_env.getRegisteredInterface( m_oid, (Type)args[ 0 ] ); + if (null == registered_proxy) + { + return dispatch_call( m_bridge_handle, method_name, args ); + } + else + { + return registered_proxy; + } + } + else if (method_name.equals( "isSame" )) + { + // boolean isSame( Object object ) + return isSame(args[0]); + } + else if (method_name.equals( "getOid" )) + { + return m_oid; + } + } + + throw new com.sun.star.uno.RuntimeException( + "[jni_uno bridge error] unexpected call on proxy " + + proxy.toString() + ": " + method.toString() ); + } + + private Boolean isSame(Object obj) { + return Boolean.valueOf(obj != null + && m_oid.equals(UnoRuntime.generateOid(obj))); + } +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/manifest b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/manifest new file mode 100644 index 000000000..7ad02e156 --- /dev/null +++ b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/manifest @@ -0,0 +1 @@ +Sealed: true diff --git a/bridges/source/jni_uno/jni_base.h b/bridges/source/jni_uno/jni_base.h new file mode 100644 index 000000000..d4a937f7a --- /dev/null +++ b/bridges/source/jni_uno/jni_base.h @@ -0,0 +1,260 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_BASE_H +#define INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_BASE_H + +#include <sal/config.h> + +#include <cassert> + +#include <jvmaccess/unovirtualmachine.hxx> +#include <jvmaccess/virtualmachine.hxx> + +#include <osl/diagnose.h> + +#include <rtl/alloc.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +#include <uno/environment.h> +#include <typelib/typedescription.h> + + +namespace jni_uno +{ + +class JNI_info; + +struct BridgeRuntimeError +{ + OUString m_message; + + explicit BridgeRuntimeError( OUString const & message ) + : m_message( message ) + {} +}; + + +class JNI_context +{ + JNI_info const * m_jni_info; + JNIEnv * m_env; + jobject m_class_loader; + + JNI_context( JNI_context const & ) = delete; + JNI_context& operator = ( JNI_context const &) = delete; + + void java_exc_occurred() const; +public: + explicit JNI_context( + JNI_info const * jni_info, JNIEnv * env, jobject class_loader ) + : m_jni_info( jni_info ), + m_env( env ), + m_class_loader( class_loader ) + {} + + JNI_info const * get_info() const + { return m_jni_info; } + + JNIEnv * operator -> () const + { return m_env; } + JNIEnv * get_jni_env() const + { return m_env; } + + // does not handle exceptions, *classClass will be null if exception + // occurred: + void getClassForName(jclass * classClass, jmethodID * methodForName) const; + + // if inException, does not handle exceptions, in which case returned value + // will be null if exception occurred: + jclass findClass( + char const * name, jclass classClass, jmethodID methodForName, + bool inException) const; + + inline void ensure_no_exception() const; // throws BridgeRuntimeError + inline bool assert_no_exception() const; // asserts and clears exception + + OUString get_stack_trace( jobject jo_exc = nullptr ) const; +}; + +inline void JNI_context::ensure_no_exception() const +{ + if (m_env->ExceptionCheck()) + { + java_exc_occurred(); + } +} + +inline bool JNI_context::assert_no_exception() const +{ + if (m_env->ExceptionCheck()) + { + SAL_WARN("bridges", "unexpected java exception occurred"); +#if OSL_DEBUG_LEVEL > 0 + m_env->ExceptionDescribe(); +#endif + m_env->ExceptionClear(); + return false; + } + return true; +} + + +class JNI_guarded_context + : private ::jvmaccess::VirtualMachine::AttachGuard, + public JNI_context +{ + JNI_guarded_context( JNI_guarded_context const & ) = delete; + JNI_guarded_context& operator = ( JNI_guarded_context const &) = delete; + +public: + explicit JNI_guarded_context( + JNI_info const * jni_info, + rtl::Reference<jvmaccess::UnoVirtualMachine> const & vm_access) + : AttachGuard( vm_access->getVirtualMachine() ), + JNI_context( + jni_info, AttachGuard::getEnvironment(), + static_cast< jobject >(vm_access->getClassLoader()) ) + {} +}; + + +class JLocalAutoRef +{ + JNI_context const & m_jni; + jobject m_jo; + +public: + explicit JLocalAutoRef( JNI_context const & jni ) + : m_jni( jni ), + m_jo( nullptr ) + {} + explicit JLocalAutoRef( JNI_context const & jni, jobject jo ) + : m_jni( jni ), + m_jo( jo ) + {} + inline JLocalAutoRef( JLocalAutoRef & auto_ref ); + inline ~JLocalAutoRef(); + + jobject get() const + { return m_jo; } + bool is() const + { return (nullptr != m_jo); } + inline jobject release(); + inline void reset( jobject jo ); + inline JLocalAutoRef & operator = ( JLocalAutoRef & auto_ref ); +}; + +inline JLocalAutoRef::~JLocalAutoRef() +{ + if (nullptr != m_jo) + m_jni->DeleteLocalRef( m_jo ); +} + +inline JLocalAutoRef::JLocalAutoRef( JLocalAutoRef & auto_ref ) + : m_jni( auto_ref.m_jni ), + m_jo( auto_ref.m_jo ) +{ + auto_ref.m_jo = nullptr; +} + +inline jobject JLocalAutoRef::release() +{ + jobject jo = m_jo; + m_jo = nullptr; + return jo; +} + +inline void JLocalAutoRef::reset( jobject jo ) +{ + if (jo != m_jo) + { + if (nullptr != m_jo) + m_jni->DeleteLocalRef( m_jo ); + m_jo = jo; + } +} + +inline JLocalAutoRef & JLocalAutoRef::operator = ( JLocalAutoRef & auto_ref ) +{ + assert( m_jni.get_jni_env() == auto_ref.m_jni.get_jni_env() ); + reset( auto_ref.m_jo ); + auto_ref.m_jo = nullptr; + return *this; +} + + + +struct rtl_mem +{ + static void * operator new ( size_t nSize ) + { return std::malloc( nSize ); } + static void operator delete ( void * mem ) + { std::free( mem ); } + static void * operator new ( size_t, void * mem ) + { return mem; } + static void operator delete ( void *, void * ) + {} + + static inline rtl_mem * allocate( std::size_t bytes ); +}; + +inline rtl_mem * rtl_mem::allocate( std::size_t bytes ) +{ + void * p = std::malloc( bytes ); + if (nullptr == p) + throw BridgeRuntimeError( "out of memory!" ); + return static_cast<rtl_mem *>(p); +} + + +class TypeDescr +{ + typelib_TypeDescription * m_td; + + TypeDescr( TypeDescr const & ) = delete; + TypeDescr& operator = ( TypeDescr const & ) = delete; + +public: + inline explicit TypeDescr( typelib_TypeDescriptionReference * td_ref ); + ~TypeDescr() + { TYPELIB_DANGER_RELEASE( m_td ); } + + typelib_TypeDescription * get() const + { return m_td; } +}; + +inline TypeDescr::TypeDescr( typelib_TypeDescriptionReference * td_ref ) + : m_td( nullptr ) +{ + TYPELIB_DANGER_GET( &m_td, td_ref ); + if (nullptr == m_td) + { + throw BridgeRuntimeError( + "cannot get comprehensive type description for " + + OUString::unacquired( &td_ref->pTypeName ) ); + } +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/bridges/source/jni_uno/jni_bridge.h b/bridges/source/jni_uno/jni_bridge.h new file mode 100644 index 000000000..730d034c6 --- /dev/null +++ b/bridges/source/jni_uno/jni_bridge.h @@ -0,0 +1,121 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_BRIDGE_H +#define INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_BRIDGE_H + +#include <sal/config.h> + +#include <atomic> +#include <cstddef> + +#include "jni_base.h" +#include "jni_helper.h" + +#include <osl/diagnose.h> + +#include <uno/mapping.h> +#include <uno/dispatcher.h> + +#include <com/sun/star/uno/XInterface.hpp> + + +namespace jni_uno +{ + +class JNI_info; +struct Bridge; + +struct Mapping : public uno_Mapping +{ + Bridge * m_bridge; +}; + +// Holds environments and mappings: +struct Bridge +{ + mutable std::atomic<std::size_t> m_ref; + + uno_ExtEnvironment * m_uno_env; + uno_Environment * m_java_env; + + Mapping m_java2uno; + Mapping m_uno2java; + bool m_registered_java2uno; + + ~Bridge(); + explicit Bridge( + uno_Environment * java_env, uno_ExtEnvironment * uno_env, + bool registered_java2uno ); + + void acquire() const; + void release() const; + + // jni_data.cxx + void map_to_uno( + JNI_context const & jni, + void * uno_data, jvalue java_data, + typelib_TypeDescriptionReference * type, + JNI_type_info const * info /* maybe 0 */, + bool assign, bool out_param, + bool special_wrapped_integral_types = false ) const; + void map_to_java( + JNI_context const & jni, + jvalue * java_data, void const * uno_data, + typelib_TypeDescriptionReference * type, + JNI_type_info const * info /* maybe 0 */, + bool in_param, bool out_param, + bool special_wrapped_integral_types = false ) const; + + // jni_uno2java.cxx + void handle_uno_exc( + JNI_context const & jni, uno_Any * uno_exc ) const; + void call_java( + jobject javaI, + typelib_InterfaceTypeDescription * iface_td, + sal_Int32 local_member_index, sal_Int32 function_pos_offset, + typelib_TypeDescriptionReference * return_type, + typelib_MethodParameter * params, sal_Int32 nParams, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const; + jobject map_to_java( + JNI_context const & jni, + uno_Interface * pUnoI, JNI_interface_type_info const * info ) const; + + // jni_java2uno.cxx + void handle_java_exc( + JNI_context const & jni, + JLocalAutoRef const & jo_exc, uno_Any * uno_exc ) const; + jobject call_uno( + JNI_context const & jni, + uno_Interface * pUnoI, typelib_TypeDescription * member_td, + typelib_TypeDescriptionReference * return_tdref, + sal_Int32 nParams, typelib_MethodParameter const * pParams, + jobjectArray jo_args ) const; + uno_Interface * map_to_uno( + JNI_context const & jni, + jobject javaI, JNI_interface_type_info const * info ) const; + + JNI_info const * getJniInfo() const; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/jni_uno/jni_data.cxx b/bridges/source/jni_uno/jni_data.cxx new file mode 100644 index 000000000..06e8467f7 --- /dev/null +++ b/bridges/source/jni_uno/jni_data.cxx @@ -0,0 +1,2476 @@ +/* -*- 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 <cassert> +#include <memory> + +#include "jni_bridge.h" + +#include <rtl/strbuf.hxx> +#include <uno/sequence2.h> + +namespace jni_uno +{ + + +static std::unique_ptr<rtl_mem> seq_allocate( + sal_Int32 nElements, sal_Int32 nSize ) +{ + std::unique_ptr< rtl_mem > seq( + rtl_mem::allocate( SAL_SEQUENCE_HEADER_SIZE + (nElements * nSize) ) ); + uno_Sequence * p = reinterpret_cast<uno_Sequence *>(seq.get()); + p->nRefCount = 1; + p->nElements = nElements; + return seq; +} + + +namespace { + +void createDefaultUnoValue( + JNI_context const & jni, void * uno_data, + typelib_TypeDescriptionReference * type, + JNI_type_info const * info /* maybe 0 */, bool assign) +{ + switch (type->eTypeClass) { + case typelib_TypeClass_BOOLEAN: + *static_cast< sal_Bool * >(uno_data) = false; + break; + + case typelib_TypeClass_BYTE: + *static_cast< sal_Int8 * >(uno_data) = 0; + break; + + case typelib_TypeClass_SHORT: + *static_cast< sal_Int16 * >(uno_data) = 0; + break; + + case typelib_TypeClass_UNSIGNED_SHORT: + *static_cast< sal_uInt16 * >(uno_data) = 0; + break; + + case typelib_TypeClass_LONG: + *static_cast< sal_Int32 * >(uno_data) = 0; + break; + + case typelib_TypeClass_UNSIGNED_LONG: + *static_cast< sal_uInt32 * >(uno_data) = 0; + break; + + case typelib_TypeClass_HYPER: + *static_cast< sal_Int64 * >(uno_data) = 0; + break; + + case typelib_TypeClass_UNSIGNED_HYPER: + *static_cast< sal_uInt64 * >(uno_data) = 0; + break; + + case typelib_TypeClass_FLOAT: + *static_cast< float * >(uno_data) = 0; + break; + + case typelib_TypeClass_DOUBLE: + *static_cast< double * >(uno_data) = 0; + break; + + case typelib_TypeClass_CHAR: + *static_cast< sal_Unicode * >(uno_data) = 0; + break; + + case typelib_TypeClass_STRING: + if (!assign) { + *static_cast< rtl_uString ** >(uno_data) = nullptr; + } + rtl_uString_new(static_cast< rtl_uString ** >(uno_data)); + break; + + case typelib_TypeClass_TYPE: + if (assign) { + typelib_typedescriptionreference_release( + *static_cast< typelib_TypeDescriptionReference ** >(uno_data)); + } + *static_cast< typelib_TypeDescriptionReference ** >(uno_data) + = *typelib_static_type_getByTypeClass(typelib_TypeClass_VOID); + assert( + *static_cast< typelib_TypeDescriptionReference ** >(uno_data) != nullptr); + typelib_typedescriptionreference_acquire( + *static_cast< typelib_TypeDescriptionReference ** >(uno_data)); + break; + + case typelib_TypeClass_ANY: + if (assign) { + uno_any_destruct(static_cast< uno_Any * >(uno_data), nullptr); + } + uno_any_construct( + static_cast< uno_Any * >(uno_data), nullptr, + jni.get_info()->m_XInterface_type_info->m_td.get(), nullptr); + break; + + case typelib_TypeClass_SEQUENCE: + { + std::unique_ptr< rtl_mem > seq(seq_allocate(0, 0)); + if (assign) { + uno_type_destructData(uno_data, type, nullptr); + } + *static_cast< uno_Sequence ** >(uno_data) + = reinterpret_cast< uno_Sequence * >(seq.release()); + break; + } + + case typelib_TypeClass_ENUM: + { + typelib_TypeDescription * td = nullptr; + TYPELIB_DANGER_GET(&td, type); + *static_cast< sal_Int32 * >(uno_data) + = (reinterpret_cast< typelib_EnumTypeDescription * >(td)-> + nDefaultEnumValue); + TYPELIB_DANGER_RELEASE(td); + break; + } + + case typelib_TypeClass_STRUCT: + { + if (info == nullptr) { + info = jni.get_info()->get_type_info(jni, type); + } + JNI_compound_type_info const * comp_info + = static_cast< JNI_compound_type_info const * >(info); + typelib_CompoundTypeDescription * comp_td + = reinterpret_cast< typelib_CompoundTypeDescription * >( + comp_info->m_td.get()); + sal_Int32 nPos = 0; + sal_Int32 nMembers = comp_td->nMembers; + try { + if (comp_td->pBaseTypeDescription != nullptr) { + createDefaultUnoValue( + jni, uno_data, + comp_td->pBaseTypeDescription->aBase.pWeakRef, + comp_info->m_base, assign); + } + for (; nPos < nMembers; ++nPos) { + createDefaultUnoValue( + jni, + (static_cast< char * >(uno_data) + + comp_td->pMemberOffsets[nPos]), + comp_td->ppTypeRefs[nPos], nullptr, assign); + } + } catch (...) { + if (!assign) { + for (sal_Int32 i = 0; i < nPos; ++i) { + uno_type_destructData( + (static_cast< char * >(uno_data) + + comp_td->pMemberOffsets[i]), + comp_td->ppTypeRefs[i], nullptr); + } + if (comp_td->pBaseTypeDescription != nullptr) { + uno_destructData( + uno_data, &comp_td->pBaseTypeDescription->aBase, nullptr); + } + } + throw; + } + } + break; + + case typelib_TypeClass_INTERFACE: + if (assign) { + uno_Interface * p = *static_cast< uno_Interface ** >(uno_data); + if (p != nullptr) { + (*p->release)(p); + } + } + *static_cast< uno_Interface ** >(uno_data) = nullptr; + break; + + default: + assert(false); + break; + } +} + +} + +void Bridge::map_to_uno( + JNI_context const & jni, + void * uno_data, jvalue java_data, + typelib_TypeDescriptionReference * type, + JNI_type_info const * info /* maybe 0 */, + bool assign, bool out_param, + bool special_wrapped_integral_types ) const +{ + assert( + !out_param || + (jni->GetArrayLength( static_cast<jarray>(java_data.l) ) == 1) ); + + switch (type->eTypeClass) + { + case typelib_TypeClass_CHAR: + if (out_param) + { + jni->GetCharArrayRegion( + static_cast<jcharArray>(java_data.l), 0, 1, static_cast<jchar *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jchar *>(uno_data) = jni->CallCharMethodA( + java_data.l, getJniInfo()->m_method_Character_charValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jchar *>(uno_data) = java_data.c; + } + break; + case typelib_TypeClass_BOOLEAN: + if (out_param) + { + jni->GetBooleanArrayRegion( + static_cast<jbooleanArray>(java_data.l), 0, 1, static_cast<jboolean *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jboolean *>(uno_data) = jni->CallBooleanMethodA( + java_data.l, getJniInfo()->m_method_Boolean_booleanValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jboolean *>(uno_data) = java_data.z; + } + break; + case typelib_TypeClass_BYTE: + if (out_param) + { + jni->GetByteArrayRegion( + static_cast<jbyteArray>(java_data.l), 0, 1, static_cast<jbyte *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jbyte *>(uno_data) = jni->CallByteMethodA( + java_data.l, getJniInfo()->m_method_Byte_byteValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jbyte *>(uno_data) = java_data.b; + } + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + if (out_param) + { + jni->GetShortArrayRegion( + static_cast<jshortArray>(java_data.l), 0, 1, static_cast<jshort *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jshort *>(uno_data) = jni->CallShortMethodA( + java_data.l, getJniInfo()->m_method_Short_shortValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jshort *>(uno_data) = java_data.s; + } + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + if (out_param) + { + jni->GetIntArrayRegion( + static_cast<jintArray>(java_data.l), 0, 1, static_cast<jint *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jint *>(uno_data) = jni->CallIntMethodA( + java_data.l, getJniInfo()->m_method_Integer_intValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jint *>(uno_data) = java_data.i; + } + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + if (out_param) + { + jni->GetLongArrayRegion( + static_cast<jlongArray>(java_data.l), 0, 1, static_cast<jlong *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jlong *>(uno_data) = jni->CallLongMethodA( + java_data.l, getJniInfo()->m_method_Long_longValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jlong *>(uno_data) = java_data.j; + } + break; + case typelib_TypeClass_FLOAT: + if (out_param) + { + jni->GetFloatArrayRegion( + static_cast<jfloatArray>(java_data.l), 0, 1, static_cast<jfloat *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jfloat *>(uno_data) = jni->CallFloatMethodA( + java_data.l, getJniInfo()->m_method_Float_floatValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jfloat *>(uno_data) = java_data.f; + } + break; + case typelib_TypeClass_DOUBLE: + if (out_param) + { + jni->GetDoubleArrayRegion( + static_cast<jdoubleArray>(java_data.l), 0, 1, static_cast<jdouble *>(uno_data) ); + jni.ensure_no_exception(); + } + else if (special_wrapped_integral_types) + { + *static_cast<jdouble *>(uno_data) = jni->CallDoubleMethodA( + java_data.l, getJniInfo()->m_method_Double_doubleValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + *static_cast<jdouble *>(uno_data) = java_data.d; + } + break; + case typelib_TypeClass_STRING: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + if (java_data.l == nullptr) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] null-ref given!" + jni.get_stack_trace() ); + } + if (! assign) + *static_cast<rtl_uString **>(uno_data) = nullptr; + jstring_to_ustring( + jni, static_cast<rtl_uString **>(uno_data), static_cast<jstring>(java_data.l) ); + break; + } + case typelib_TypeClass_TYPE: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + if (java_data.l == nullptr) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] null-ref given!" + jni.get_stack_trace() ); + } + + // type name + JLocalAutoRef jo_type_name( + jni, jni->GetObjectField( + java_data.l, getJniInfo()->m_field_Type_typeName ) ); + if (! jo_type_name.is()) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] incomplete type object: no type name!" + + jni.get_stack_trace() ); + } + OUString type_name( + jstring_to_oustring( jni, static_cast<jstring>(jo_type_name.get()) ) ); + ::com::sun::star::uno::TypeDescription td( type_name ); + if (! td.is()) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] UNO type not found: " + type_name + + jni.get_stack_trace() ); + } + typelib_typedescriptionreference_acquire( td.get()->pWeakRef ); + if (assign) + { + typelib_typedescriptionreference_release( + *static_cast<typelib_TypeDescriptionReference **>(uno_data) ); + } + *static_cast<typelib_TypeDescriptionReference **>(uno_data) = td.get()->pWeakRef; + break; + } + case typelib_TypeClass_ANY: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + + uno_Any * pAny = static_cast<uno_Any *>(uno_data); + if (java_data.l == nullptr) // null-ref maps to XInterface null-ref + { + if (assign) + uno_any_destruct( pAny, nullptr ); + uno_any_construct( + pAny, nullptr, getJniInfo()->m_XInterface_type_info->m_td.get(), nullptr ); + break; + } + + JLocalAutoRef jo_type( jni ); + JLocalAutoRef jo_wrapped_holder( jni ); + + if (jni->IsInstanceOf( java_data.l, getJniInfo()->m_class_Any )) + { + // boxed any + jo_type.reset( jni->GetObjectField( + java_data.l, getJniInfo()->m_field_Any_type ) ); + if (! jo_type.is()) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] no type set at com.sun.star.uno.Any!" + + jni.get_stack_trace() ); + } + // wrapped value + jo_wrapped_holder.reset( + jni->GetObjectField( + java_data.l, getJniInfo()->m_field_Any_object ) ); + java_data.l = jo_wrapped_holder.get(); + } + else + { + // create type out of class + JLocalAutoRef jo_class( jni, jni->GetObjectClass( java_data.l ) ); + jo_type.reset( create_type( jni, static_cast<jclass>(jo_class.get()) ) ); + } + + // get type name + JLocalAutoRef jo_type_name( + jni, jni->GetObjectField( + jo_type.get(), getJniInfo()->m_field_Type_typeName ) ); + jni.ensure_no_exception(); + OUString type_name( + jstring_to_oustring( jni, static_cast<jstring>(jo_type_name.get()) ) ); + + ::com::sun::star::uno::TypeDescription value_td( type_name ); + if (! value_td.is()) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] UNO type not found: " + type_name + + jni.get_stack_trace() ); + } + typelib_TypeClass type_class = value_td.get()->eTypeClass; + + if (assign) + { + uno_any_destruct( pAny, nullptr ); + } + try + { + switch (type_class) + { + case typelib_TypeClass_VOID: + pAny->pData = &pAny->pReserved; + break; + case typelib_TypeClass_CHAR: + pAny->pData = &pAny->pReserved; + *static_cast<jchar *>(pAny->pData) = jni->CallCharMethodA( + java_data.l, getJniInfo()->m_method_Character_charValue, nullptr ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_BOOLEAN: + pAny->pData = &pAny->pReserved; + *static_cast<jboolean *>(pAny->pData) = jni->CallBooleanMethodA( + java_data.l, getJniInfo()->m_method_Boolean_booleanValue, nullptr ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_BYTE: + pAny->pData = &pAny->pReserved; + *static_cast<jbyte *>(pAny->pData) = jni->CallByteMethodA( + java_data.l, getJniInfo()->m_method_Byte_byteValue, nullptr ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + pAny->pData = &pAny->pReserved; + *static_cast<jshort *>(pAny->pData) = jni->CallShortMethodA( + java_data.l, getJniInfo()->m_method_Short_shortValue, nullptr ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + pAny->pData = &pAny->pReserved; + *static_cast<jint *>(pAny->pData) = jni->CallIntMethodA( + java_data.l, getJniInfo()->m_method_Integer_intValue, nullptr ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + if (sizeof (sal_Int64) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *static_cast<jlong *>(pAny->pData) = jni->CallLongMethodA( + java_data.l, getJniInfo()->m_method_Long_longValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + std::unique_ptr< rtl_mem > mem( + rtl_mem::allocate( sizeof (sal_Int64) ) ); + *reinterpret_cast<jlong *>(mem.get()) = jni->CallLongMethodA( + java_data.l, getJniInfo()->m_method_Long_longValue, nullptr ); + jni.ensure_no_exception(); + pAny->pData = mem.release(); + } + break; + case typelib_TypeClass_FLOAT: + if (sizeof (float) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *static_cast<jfloat *>(pAny->pData) = jni->CallFloatMethodA( + java_data.l, getJniInfo()->m_method_Float_floatValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + std::unique_ptr< rtl_mem > mem( + rtl_mem::allocate( sizeof (float) ) ); + *reinterpret_cast<jfloat *>(mem.get()) = jni->CallFloatMethodA( + java_data.l, getJniInfo()->m_method_Float_floatValue, nullptr ); + jni.ensure_no_exception(); + pAny->pData = mem.release(); + } + break; + case typelib_TypeClass_DOUBLE: + if (sizeof (double) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *static_cast<jdouble *>(pAny->pData) = + jni->CallDoubleMethodA( + java_data.l, + getJniInfo()->m_method_Double_doubleValue, nullptr ); + jni.ensure_no_exception(); + } + else + { + std::unique_ptr< rtl_mem > mem( + rtl_mem::allocate( sizeof (double) ) ); + *reinterpret_cast<jdouble *>(mem.get()) = + jni->CallDoubleMethodA( + java_data.l, + getJniInfo()->m_method_Double_doubleValue, nullptr ); + jni.ensure_no_exception(); + pAny->pData = mem.release(); + } + break; + case typelib_TypeClass_STRING: + // opt: anies often contain strings; copy string directly + pAny->pReserved = nullptr; + pAny->pData = &pAny->pReserved; + jstring_to_ustring( + jni, static_cast<rtl_uString **>(pAny->pData), + static_cast<jstring>(java_data.l) ); + break; + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ENUM: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_INTERFACE: + pAny->pData = &pAny->pReserved; + map_to_uno( + jni, pAny->pData, java_data, + value_td.get()->pWeakRef, nullptr, + false /* no assign */, false /* no out param */ ); + break; + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + std::unique_ptr< rtl_mem > mem( + rtl_mem::allocate( value_td.get()->nSize ) ); + map_to_uno( + jni, mem.get(), java_data, value_td.get()->pWeakRef, nullptr, + false /* no assign */, false /* no out param */ ); + pAny->pData = mem.release(); + break; + } + default: + { + throw BridgeRuntimeError( + "[map_to_uno():" + type_name + + "] unsupported value type of any!" + + jni.get_stack_trace() ); + } + } + } + catch (...) + { + if (assign) + { + // restore to valid any + uno_any_construct( pAny, nullptr, nullptr, nullptr ); + } + throw; + } + typelib_typedescriptionreference_acquire( value_td.get()->pWeakRef ); + pAny->pType = value_td.get()->pWeakRef; + break; + } + case typelib_TypeClass_ENUM: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + if (java_data.l == nullptr) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] null-ref given!" + jni.get_stack_trace() ); + } + + *static_cast<jint *>(uno_data) = jni->GetIntField( + java_data.l, getJniInfo()->m_field_Enum_m_value ); + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + if (java_data.l == nullptr) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] null-ref given!" + jni.get_stack_trace() ); + } + + if (info == nullptr) + info = getJniInfo()->get_type_info( jni, type ); + JNI_compound_type_info const * comp_info = + static_cast< JNI_compound_type_info const * >( info ); + + typelib_CompoundTypeDescription * comp_td = + reinterpret_cast<typelib_CompoundTypeDescription *>(comp_info->m_td.get()); + bool polymorphic + = comp_td->aBase.eTypeClass == typelib_TypeClass_STRUCT + && reinterpret_cast< typelib_StructTypeDescription * >( + comp_td)->pParameterizedTypes != nullptr; + + sal_Int32 nPos = 0; + sal_Int32 nMembers = comp_td->nMembers; + try + { + if (comp_td->pBaseTypeDescription != nullptr) + { + map_to_uno( + jni, uno_data, java_data, + comp_td->pBaseTypeDescription->aBase.pWeakRef, + comp_info->m_base, + assign, false /* no out param */ ); + } + + for ( ; nPos < nMembers; ++nPos ) + { + void * p = static_cast<char *>(uno_data) + comp_td->pMemberOffsets[ nPos ]; + typelib_TypeDescriptionReference * member_type = + comp_td->ppTypeRefs[ nPos ]; + jfieldID field_id = comp_info->m_fields[ nPos ]; + bool parameterizedType = polymorphic + && reinterpret_cast< typelib_StructTypeDescription * >( + comp_td)->pParameterizedTypes[nPos]; + switch (member_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jchar *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jchar *>(p) = jni->GetCharField( + java_data.l, field_id ); + } + break; + case typelib_TypeClass_BOOLEAN: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jboolean *>(p) = false; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jboolean *>(p) = jni->GetBooleanField( + java_data.l, field_id ); + } + break; + case typelib_TypeClass_BYTE: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jbyte *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jbyte *>(p) = jni->GetByteField( + java_data.l, field_id ); + } + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jshort *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jshort *>(p) = jni->GetShortField( + java_data.l, field_id ); + } + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jint *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jint *>(p) = jni->GetIntField( java_data.l, field_id ); + } + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jlong *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jlong *>(p) = jni->GetLongField( + java_data.l, field_id ); + } + break; + case typelib_TypeClass_FLOAT: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jfloat *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jfloat *>(p) = jni->GetFloatField( + java_data.l, field_id ); + } + break; + case typelib_TypeClass_DOUBLE: + if (parameterizedType) { + JLocalAutoRef jo( + jni, jni->GetObjectField( java_data.l, field_id ) ); + if ( jo.get() == nullptr ) { + *static_cast<jdouble *>(p) = 0; + } else { + jvalue val; + val.l = jo.get(); + map_to_uno( + jni, p, val, member_type, nullptr, assign, false, + true ); + } + } else { + *static_cast<jdouble *>(p) = jni->GetDoubleField( + java_data.l, field_id ); + } + break; + default: + { + JLocalAutoRef jo_field( jni ); + bool checkNull; + if (field_id == nullptr) + { + // special for Message: call Throwable.getMessage() + assert( + type_equals( + type, + getJniInfo()->m_Exception_type.getTypeLibType() ) + || type_equals( + type, + getJniInfo()->m_RuntimeException_type. + getTypeLibType() ) ); + assert( nPos == 0 ); // first member + // call getMessage() + jo_field.reset( + jni->CallObjectMethodA( + java_data.l, + getJniInfo()->m_method_Throwable_getMessage, nullptr ) + ); + jni.ensure_no_exception(); + checkNull = true; + } + else + { + jo_field.reset( + jni->GetObjectField( java_data.l, field_id ) ); + checkNull = parameterizedType; + } + if (checkNull && !jo_field.is()) { + createDefaultUnoValue(jni, p, member_type, nullptr, assign); + } else { + jvalue val; + val.l = jo_field.get(); + map_to_uno( + jni, p, val, member_type, nullptr, + assign, false /* no out param */ ); + } + break; + } + } + } + } + catch (...) + { + if (! assign) + { + // cleanup + for ( sal_Int32 nCleanup = 0; nCleanup < nPos; ++nCleanup ) + { + void * p = + static_cast<char *>(uno_data) + comp_td->pMemberOffsets[ nCleanup ]; + uno_type_destructData( + p, comp_td->ppTypeRefs[ nCleanup ], nullptr ); + } + if (comp_td->pBaseTypeDescription != nullptr) + { + uno_destructData( + uno_data, &comp_td->pBaseTypeDescription->aBase, nullptr ); + } + } + throw; + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + if (java_data.l == nullptr) + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] null-ref given!" + jni.get_stack_trace() ); + } + + TypeDescr td( type ); + typelib_TypeDescriptionReference * element_type = + reinterpret_cast<typelib_IndirectTypeDescription *>(td.get())->pType; + + std::unique_ptr< rtl_mem > seq; + sal_Int32 nElements = jni->GetArrayLength( static_cast<jarray>(java_data.l) ); + + switch (element_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + seq = seq_allocate( nElements, sizeof (sal_Unicode) ); + jni->GetCharArrayRegion( + static_cast<jcharArray>(java_data.l), 0, nElements, + reinterpret_cast<jchar *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_BOOLEAN: + seq = seq_allocate( nElements, sizeof (sal_Bool) ); + jni->GetBooleanArrayRegion( + static_cast<jbooleanArray>(java_data.l), 0, nElements, + reinterpret_cast<jboolean *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_BYTE: + seq = seq_allocate( nElements, sizeof (sal_Int8) ); + jni->GetByteArrayRegion( + static_cast<jbyteArray>(java_data.l), 0, nElements, + reinterpret_cast<jbyte *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + seq = seq_allocate( nElements, sizeof (sal_Int16) ); + jni->GetShortArrayRegion( + static_cast<jshortArray>(java_data.l), 0, nElements, + reinterpret_cast<jshort *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + seq = seq_allocate( nElements, sizeof (sal_Int32) ); + jni->GetIntArrayRegion( + static_cast<jintArray>(java_data.l), 0, nElements, + reinterpret_cast<jint *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + seq = seq_allocate( nElements, sizeof (sal_Int64) ); + jni->GetLongArrayRegion( + static_cast<jlongArray>(java_data.l), 0, nElements, + reinterpret_cast<jlong *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_FLOAT: + seq = seq_allocate( nElements, sizeof (float) ); + jni->GetFloatArrayRegion( + static_cast<jfloatArray>(java_data.l), 0, nElements, + reinterpret_cast<jfloat *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_DOUBLE: + seq = seq_allocate( nElements, sizeof (double) ); + jni->GetDoubleArrayRegion( + static_cast<jdoubleArray>(java_data.l), 0, nElements, + reinterpret_cast<jdouble *>(reinterpret_cast<uno_Sequence *>(seq.get())->elements) ); + jni.ensure_no_exception(); + break; + case typelib_TypeClass_STRING: + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ANY: + case typelib_TypeClass_ENUM: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_INTERFACE: + { + TypeDescr element_td( element_type ); + seq = seq_allocate( nElements, element_td.get()->nSize ); + + JNI_type_info const * element_info; + if (element_type->eTypeClass == typelib_TypeClass_STRUCT || + element_type->eTypeClass == typelib_TypeClass_EXCEPTION || + element_type->eTypeClass == typelib_TypeClass_INTERFACE) + { + element_info = + getJniInfo()->get_type_info( jni, element_td.get() ); + } + else + { + element_info = nullptr; + } + + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + try + { + JLocalAutoRef jo( + jni, jni->GetObjectArrayElement( + static_cast<jobjectArray>(java_data.l), nPos ) ); + jni.ensure_no_exception(); + jvalue val; + val.l = jo.get(); + void * p = + reinterpret_cast<uno_Sequence *>(seq.get())->elements + + (nPos * element_td.get()->nSize); + map_to_uno( + jni, p, val, element_td.get()->pWeakRef, element_info, + false /* no assign */, false /* no out param */ ); + } + catch (...) + { + // cleanup + for ( sal_Int32 nCleanPos = 0; + nCleanPos < nPos; ++nCleanPos ) + { + void * p = + reinterpret_cast<uno_Sequence *>(seq.get())->elements + + (nCleanPos * element_td.get()->nSize); + uno_destructData( p, element_td.get(), nullptr ); + } + throw; + } + } + break; + } + default: + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported sequence element type: " + + OUString::unacquired( &element_type->pTypeName ) + + jni.get_stack_trace() ); + } + } + + if (assign) + uno_destructData( uno_data, td.get(), nullptr ); + *static_cast<uno_Sequence **>(uno_data) = reinterpret_cast<uno_Sequence *>(seq.release()); + break; + } + case typelib_TypeClass_INTERFACE: + { + JLocalAutoRef jo_out_holder( jni ); + if (out_param) + { + jo_out_holder.reset( + jni->GetObjectArrayElement( static_cast<jobjectArray>(java_data.l), 0 ) ); + jni.ensure_no_exception(); + java_data.l = jo_out_holder.get(); + } + + if (java_data.l == nullptr) // null-ref + { + if (assign) + { + uno_Interface * p = *static_cast<uno_Interface **>(uno_data); + if (p != nullptr) + (*p->release)( p ); + } + *static_cast<uno_Interface **>(uno_data) = nullptr; + } + else + { + if (info == nullptr) + info = getJniInfo()->get_type_info( jni, type ); + JNI_interface_type_info const * iface_info = + static_cast< JNI_interface_type_info const * >( info ); + uno_Interface * pUnoI = map_to_uno( jni, java_data.l, iface_info ); + if (assign) + { + uno_Interface * p = *static_cast<uno_Interface **>(uno_data); + if (p != nullptr) + (*p->release)( p ); + } + *static_cast<uno_Interface **>(uno_data) = pUnoI; + } + break; + } + default: + { + throw BridgeRuntimeError( + "[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported type!" + jni.get_stack_trace() ); + } + } +} + + +void Bridge::map_to_java( + JNI_context const & jni, + jvalue * java_data, void const * uno_data, + typelib_TypeDescriptionReference * type, + JNI_type_info const * info /* maybe 0 */, + bool in_param, bool out_param, + bool special_wrapped_integral_types ) const +{ + // 4th param of Set*ArrayRegion changed from pointer to non-const to pointer + // to const between <http://docs.oracle.com/javase/6/docs/technotes/guides/ + // jni/spec/functions.html#wp22933> and <http://docs.oracle.com/javase/7/ + // docs/technotes/guides/jni/spec/functions.html#wp22933>; work around that + // difference in a way that doesn't trigger loplugin:redundantcast: + void * data = const_cast<void *>(uno_data); + + switch (type->eTypeClass) + { + case typelib_TypeClass_CHAR: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewCharArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetCharArrayRegion( + static_cast<jcharArray>(jo_ar.get()), 0, 1, static_cast<jchar *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetCharArrayRegion( + static_cast<jcharArray>(java_data->l), 0, 1, static_cast<jchar *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.c = *static_cast<jchar const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Character, + getJniInfo()->m_ctor_Character_with_char, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->c = *static_cast<jchar const *>(uno_data); + } + break; + case typelib_TypeClass_BOOLEAN: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewBooleanArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetBooleanArrayRegion( + static_cast<jbooleanArray>(jo_ar.get()), + 0, 1, static_cast<jboolean *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetBooleanArrayRegion( + static_cast<jbooleanArray>(java_data->l), + 0, 1, static_cast<jboolean *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.z = *static_cast<jboolean const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Boolean, + getJniInfo()->m_ctor_Boolean_with_boolean, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->z = *static_cast<jboolean const *>(uno_data); + } + break; + case typelib_TypeClass_BYTE: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewByteArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetByteArrayRegion( + static_cast<jbyteArray>(jo_ar.get()), 0, 1, static_cast<jbyte *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetByteArrayRegion( + static_cast<jbyteArray>(java_data->l), 0, 1, static_cast<jbyte *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.b = *static_cast<jbyte const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Byte, + getJniInfo()->m_ctor_Byte_with_byte, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->b = *static_cast<jbyte const *>(uno_data); + } + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewShortArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetShortArrayRegion( + static_cast<jshortArray>(jo_ar.get()), 0, 1, static_cast<jshort *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetShortArrayRegion( + static_cast<jshortArray>(java_data->l), 0, 1, static_cast<jshort *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.s = *static_cast<jshort const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Short, + getJniInfo()->m_ctor_Short_with_short, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->s = *static_cast<jshort const *>(uno_data); + } + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewIntArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetIntArrayRegion( + static_cast<jintArray>(jo_ar.get()), 0, 1, static_cast<jint *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetIntArrayRegion( + static_cast<jintArray>(java_data->l), 0, 1, static_cast<jint *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.i = *static_cast<jint const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Integer, + getJniInfo()->m_ctor_Integer_with_int, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->i = *static_cast<jint const *>(uno_data); + } + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewLongArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetLongArrayRegion( + static_cast<jlongArray>(jo_ar.get()), 0, 1, static_cast<jlong *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetLongArrayRegion( + static_cast<jlongArray>(java_data->l), 0, 1, static_cast<jlong *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.j = *static_cast<jlong const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Long, + getJniInfo()->m_ctor_Long_with_long, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->j = *static_cast<jlong const *>(uno_data); + } + break; + case typelib_TypeClass_FLOAT: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewFloatArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetFloatArrayRegion( + static_cast<jfloatArray>(jo_ar.get()), 0, 1, static_cast<jfloat *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetFloatArrayRegion( + static_cast<jfloatArray>(java_data->l), 0, 1, static_cast<jfloat *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.f = *static_cast<jfloat const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Float, + getJniInfo()->m_ctor_Float_with_float, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->f = *static_cast<jfloat const *>(uno_data); + } + break; + case typelib_TypeClass_DOUBLE: + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_ar( jni, jni->NewDoubleArray( 1 ) ); + jni.ensure_no_exception(); + if (in_param) + { + jni->SetDoubleArrayRegion( + static_cast<jdoubleArray>(jo_ar.get()), + 0, 1, static_cast<jdouble *>(data) ); + jni.ensure_no_exception(); + } + java_data->l = jo_ar.release(); + } + else + { + if (in_param) + { + jni->SetDoubleArrayRegion( + static_cast<jdoubleArray>(java_data->l), + 0, 1, static_cast<jdouble *>(data) ); + jni.ensure_no_exception(); + } + } + } + else if (special_wrapped_integral_types) + { + jvalue arg; + arg.d = *static_cast<double const *>(uno_data); + java_data->l = jni->NewObjectA( + getJniInfo()->m_class_Double, + getJniInfo()->m_ctor_Double_with_double, &arg ); + jni.ensure_no_exception(); + } + else + { + java_data->d = *static_cast<jdouble const *>(uno_data); + } + break; + case typelib_TypeClass_STRING: + { + if (out_param) + { + JLocalAutoRef jo_in( jni ); + if (in_param) + { + jo_in.reset( + ustring_to_jstring( + jni, *static_cast<rtl_uString * const *>(uno_data) ) ); + } + if (java_data->l == nullptr) + { + java_data->l = jni->NewObjectArray( + 1, getJniInfo()->m_class_String, jo_in.get() ); + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_in.get() ); + jni.ensure_no_exception(); + } + } + else + { + assert( in_param ); + java_data->l = + ustring_to_jstring( jni, *static_cast<rtl_uString * const *>(uno_data) ); + } + break; + } + case typelib_TypeClass_TYPE: + { + if (out_param) + { + JLocalAutoRef jo_in( jni ); + if (in_param) + { + jo_in.reset( + create_type( + jni, + *static_cast<typelib_TypeDescriptionReference * const *>(uno_data) ) + ); + } + if (java_data->l == nullptr) + { + java_data->l = jni->NewObjectArray( + 1, getJniInfo()->m_class_Type, jo_in.get() ); + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_in.get() ); + jni.ensure_no_exception(); + } + } + else + { + assert( in_param ); + java_data->l = + create_type( + jni, + *static_cast<typelib_TypeDescriptionReference * const *>(uno_data) ); + } + break; + } + case typelib_TypeClass_ANY: + { + JLocalAutoRef jo_any( jni ); + if (in_param) + { + uno_Any const * pAny = static_cast<uno_Any const *>(uno_data); + switch (pAny->pType->eTypeClass) + { + case typelib_TypeClass_VOID: + jo_any.reset( + jni->NewLocalRef( getJniInfo()->m_object_Any_VOID ) ); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + { + jvalue args[ 2 ]; + args[ 0 ].s = *static_cast<jshort const *>(pAny->pData); + JLocalAutoRef jo_val( + jni, jni->NewObjectA( + getJniInfo()->m_class_Short, + getJniInfo()->m_ctor_Short_with_short, args ) ); + jni.ensure_no_exception(); + // box up in com.sun.star.uno.Any + args[ 0 ].l = getJniInfo()->m_object_Type_UNSIGNED_SHORT; + args[ 1 ].l = jo_val.get(); + jo_any.reset( + jni->NewObjectA( + getJniInfo()->m_class_Any, + getJniInfo()->m_ctor_Any_with_Type_Object, args ) ); + jni.ensure_no_exception(); + break; + } + case typelib_TypeClass_UNSIGNED_LONG: + { + jvalue args[ 2 ]; + args[ 0 ].i = *static_cast<jint const *>(pAny->pData); + JLocalAutoRef jo_val( + jni, jni->NewObjectA( + getJniInfo()->m_class_Integer, + getJniInfo()->m_ctor_Integer_with_int, args ) ); + jni.ensure_no_exception(); + // box up in com.sun.star.uno.Any + args[ 0 ].l = getJniInfo()->m_object_Type_UNSIGNED_LONG; + args[ 1 ].l = jo_val.get(); + jo_any.reset( + jni->NewObjectA( + getJniInfo()->m_class_Any, + getJniInfo()->m_ctor_Any_with_Type_Object, args ) ); + jni.ensure_no_exception(); + break; + } + case typelib_TypeClass_UNSIGNED_HYPER: + { + jvalue args[ 2 ]; + args[ 0 ].j = *static_cast<jlong const *>(pAny->pData); + JLocalAutoRef jo_val( + jni, jni->NewObjectA( + getJniInfo()->m_class_Long, + getJniInfo()->m_ctor_Long_with_long, args ) ); + jni.ensure_no_exception(); + // box up in com.sun.star.uno.Any + args[ 0 ].l = getJniInfo()->m_object_Type_UNSIGNED_HYPER; + args[ 1 ].l = jo_val.get(); + jo_any.reset( + jni->NewObjectA( + getJniInfo()->m_class_Any, + getJniInfo()->m_ctor_Any_with_Type_Object, args ) ); + jni.ensure_no_exception(); + break; + } + case typelib_TypeClass_STRING: // opt strings + jo_any.reset( ustring_to_jstring( + jni, static_cast<rtl_uString *>(pAny->pReserved) ) ); + break; + case typelib_TypeClass_SEQUENCE: + { + jvalue java_data2; + // prefetch sequence td + TypeDescr seq_td( pAny->pType ); + map_to_java( + jni, &java_data2, pAny->pData, seq_td.get()->pWeakRef, nullptr, + true /* in */, false /* no out */, + true /* create integral wrappers */ ); + jo_any.reset( java_data2.l ); + + // determine inner element type + ::com::sun::star::uno::Type element_type( + reinterpret_cast<typelib_IndirectTypeDescription *>(seq_td.get())->pType ); + while (element_type.getTypeLibType()->eTypeClass == + typelib_TypeClass_SEQUENCE) + { + TypeDescr element_td( element_type.getTypeLibType() ); + typelib_typedescriptionreference_assign( + reinterpret_cast< typelib_TypeDescriptionReference ** >( + &element_type ), + reinterpret_cast<typelib_IndirectTypeDescription *>(element_td.get()) + ->pType ); + } + // box up only if unsigned element type + switch (element_type.getTypeLibType()->eTypeClass) + { + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_UNSIGNED_HYPER: + { + jvalue args[ 2 ]; + JLocalAutoRef jo_type( + jni, create_type( jni, seq_td.get()->pWeakRef ) ); + args[ 0 ].l = jo_type.get(); + args[ 1 ].l = jo_any.get(); + jo_any.reset( + jni->NewObjectA( + getJniInfo()->m_class_Any, + getJniInfo()->m_ctor_Any_with_Type_Object, args ) ); + jni.ensure_no_exception(); + break; + } + default: + break; + } + break; + } + case typelib_TypeClass_INTERFACE: + { + uno_Interface * pUnoI = static_cast<uno_Interface *>(pAny->pReserved); + if (is_XInterface( pAny->pType )) + { + if (pUnoI != nullptr) + { + jo_any.reset( + map_to_java( + jni, pUnoI, + getJniInfo()->m_XInterface_type_info ) ); + } + // else: empty XInterface ref maps to null-ref + } + else + { + JNI_interface_type_info const * iface_info = + static_cast< JNI_interface_type_info const * >( + getJniInfo()->get_type_info( jni, pAny->pType ) ); + if (pUnoI != nullptr) + { + jo_any.reset( map_to_java( jni, pUnoI, iface_info ) ); + } + // box up in com.sun.star.uno.Any + jvalue args[ 2 ]; + args[ 0 ].l = iface_info->m_type; + args[ 1 ].l = jo_any.get(); + jo_any.reset( + jni->NewObjectA( + getJniInfo()->m_class_Any, + getJniInfo()->m_ctor_Any_with_Type_Object, args ) ); + jni.ensure_no_exception(); + } + break; + } + case typelib_TypeClass_STRUCT: + { + // Do not lose information about type arguments of instantiated + // polymorphic struct types: + OUString const & name = OUString::unacquired( + &pAny->pType->pTypeName); + assert(!name.isEmpty()); + if (name[name.getLength() - 1] == '>') + { + // Box up in com.sun.star.uno.Any: + JLocalAutoRef jo_type(jni, create_type(jni, pAny->pType)); + jvalue java_data2; + map_to_java( + jni, &java_data2, pAny->pData, pAny->pType, nullptr, true, + false); + jo_any.reset(java_data2.l); + jvalue args[2]; + args[0].l = jo_type.get(); + args[1].l = jo_any.get(); + jo_any.reset( + jni->NewObjectA( + getJniInfo()->m_class_Any, + getJniInfo()->m_ctor_Any_with_Type_Object, args)); + jni.ensure_no_exception(); + break; + } + [[fallthrough]]; + } + default: + { + jvalue java_data2; + map_to_java( + jni, &java_data2, pAny->pData, pAny->pType, nullptr, + true /* in */, false /* no out */, + true /* create integral wrappers */ ); + jo_any.reset( java_data2.l ); + break; + } + } + } + + if (out_param) + { + if (java_data->l == nullptr) + { + java_data->l = jni->NewObjectArray( + 1, getJniInfo()->m_class_Object, jo_any.get() ); + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_any.get() ); + jni.ensure_no_exception(); + } + } + else + { + java_data->l = jo_any.release(); + } + break; + } + case typelib_TypeClass_ENUM: + { + OUString const & type_name = OUString::unacquired( &type->pTypeName ); + OString class_name( + OUStringToOString( type_name, RTL_TEXTENCODING_JAVA_UTF8 ) ); + JLocalAutoRef jo_enum_class( + jni, find_class( jni, class_name.getStr() ) ); + + JLocalAutoRef jo_enum( jni ); + if (in_param) + { + // call static <enum_class>.fromInt( int ) + OString sig = "(I)L" + class_name.replace( '.', '/' ) + ";"; + jmethodID method_id = jni->GetStaticMethodID( + static_cast<jclass>(jo_enum_class.get()), "fromInt", sig.getStr() ); + jni.ensure_no_exception(); + assert( method_id != nullptr ); + + jvalue arg; + arg.i = *static_cast<jint const *>(uno_data); + jo_enum.reset( + jni->CallStaticObjectMethodA( + static_cast<jclass>(jo_enum_class.get()), method_id, &arg ) ); + jni.ensure_no_exception(); + } + if (out_param) + { + if (java_data->l == nullptr) + { + java_data->l = jni->NewObjectArray( + 1, static_cast<jclass>(jo_enum_class.get()), jo_enum.get() ); + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_enum.get() ); + jni.ensure_no_exception(); + } + } + else + { + java_data->l = jo_enum.release(); + } + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + if (info == nullptr) + info = getJniInfo()->get_type_info( jni, type ); + JNI_compound_type_info const * comp_info = + static_cast< JNI_compound_type_info const * >( info ); + + JLocalAutoRef jo_comp( jni ); + if (in_param) + { + if (type->eTypeClass == typelib_TypeClass_EXCEPTION) + { + JLocalAutoRef jo_message( + jni, ustring_to_jstring( jni, *static_cast<rtl_uString * const *>(uno_data) ) ); + jvalue arg; + arg.l = jo_message.get(); + jo_comp.reset( + jni->NewObjectA( + comp_info->m_class, comp_info->m_exc_ctor, &arg ) ); + jni.ensure_no_exception(); + } + else + { + jo_comp.reset( jni->AllocObject( comp_info->m_class ) ); + jni.ensure_no_exception(); + } + + for ( JNI_compound_type_info const * linfo = comp_info; + linfo != nullptr; + linfo = static_cast< JNI_compound_type_info const * >( + linfo->m_base ) ) + { + typelib_CompoundTypeDescription * comp_td = + reinterpret_cast<typelib_CompoundTypeDescription *>(linfo->m_td.get()); + typelib_TypeDescriptionReference ** ppMemberTypeRefs = + comp_td->ppTypeRefs; + sal_Int32 * pMemberOffsets = comp_td->pMemberOffsets; + bool polymorphic + = comp_td->aBase.eTypeClass == typelib_TypeClass_STRUCT + && reinterpret_cast< typelib_StructTypeDescription * >( + comp_td)->pParameterizedTypes != nullptr; + for ( sal_Int32 nPos = comp_td->nMembers; nPos--; ) + { + jfieldID field_id = linfo->m_fields[ nPos ]; + if (field_id != nullptr) + { + void const * p = + static_cast<char const *>(uno_data) + pMemberOffsets[ nPos ]; + typelib_TypeDescriptionReference * member_type = + ppMemberTypeRefs[ nPos ]; + bool parameterizedType = polymorphic + && (reinterpret_cast< + typelib_StructTypeDescription * >(comp_td)-> + pParameterizedTypes[nPos]); + switch (member_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + if (parameterizedType) { + jvalue arg; + arg.c = *static_cast<jchar const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Character, + getJniInfo()->m_ctor_Character_with_char, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetCharField( + jo_comp.get(), + field_id, *static_cast<jchar const *>(p) ); + } + break; + case typelib_TypeClass_BOOLEAN: + if (parameterizedType) { + jvalue arg; + arg.z = *static_cast<jboolean const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Boolean, + getJniInfo()->m_ctor_Boolean_with_boolean, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetBooleanField( + jo_comp.get(), + field_id, *static_cast<jboolean const *>(p) ); + } + break; + case typelib_TypeClass_BYTE: + if (parameterizedType) { + jvalue arg; + arg.b = *static_cast<jbyte const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Byte, + getJniInfo()->m_ctor_Byte_with_byte, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetByteField( + jo_comp.get(), + field_id, *static_cast<jbyte const *>(p) ); + } + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + if (parameterizedType) { + jvalue arg; + arg.s = *static_cast<jshort const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Short, + getJniInfo()->m_ctor_Short_with_short, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetShortField( + jo_comp.get(), + field_id, *static_cast<jshort const *>(p) ); + } + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + if (parameterizedType) { + jvalue arg; + arg.i = *static_cast<jint const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Integer, + getJniInfo()->m_ctor_Integer_with_int, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetIntField( + jo_comp.get(), + field_id, *static_cast<jint const *>(p) ); + } + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + if (parameterizedType) { + jvalue arg; + arg.j = *static_cast<jlong const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Long, + getJniInfo()->m_ctor_Long_with_long, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetLongField( + jo_comp.get(), + field_id, *static_cast<jlong const *>(p) ); + } + break; + case typelib_TypeClass_FLOAT: + if (parameterizedType) { + jvalue arg; + arg.f = *static_cast<jfloat const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Float, + getJniInfo()->m_ctor_Float_with_float, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetFloatField( + jo_comp.get(), + field_id, *static_cast<jfloat const *>(p) ); + } + break; + case typelib_TypeClass_DOUBLE: + if (parameterizedType) { + jvalue arg; + arg.d = *static_cast<jdouble const *>(p); + JLocalAutoRef jo( + jni, + jni->NewObjectA( + getJniInfo()->m_class_Double, + getJniInfo()->m_ctor_Double_with_double, + &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectField( + jo_comp.get(), field_id, jo.get() ); + } else { + jni->SetDoubleField( + jo_comp.get(), + field_id, *static_cast<jdouble const *>(p) ); + } + break; + case typelib_TypeClass_STRING: // string opt here + { + JLocalAutoRef jo_string( + jni, ustring_to_jstring( + jni, *static_cast<rtl_uString * const *>(p) ) ); + jni->SetObjectField( + jo_comp.get(), field_id, jo_string.get() ); + break; + } + default: + { + jvalue java_data2; + map_to_java( + jni, &java_data2, p, member_type, nullptr, + true /* in */, false /* no out */ ); + JLocalAutoRef jo_obj( jni, java_data2.l ); + jni->SetObjectField( + jo_comp.get(), field_id, jo_obj.get() ); + break; + } + } + } + } + } + } + if (out_param) + { + if (java_data->l == nullptr) + { + java_data->l = + jni->NewObjectArray( 1, comp_info->m_class, jo_comp.get() ); + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_comp.get() ); + jni.ensure_no_exception(); + } + } + else + { + java_data->l = jo_comp.release(); + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + // xxx todo: possible opt for pure out sequences + JLocalAutoRef jo_ar( jni ); + + sal_Int32 nElements; + uno_Sequence * seq = nullptr; + if (in_param) + { + seq = *static_cast<uno_Sequence * const *>(uno_data); + nElements = seq->nElements; + } + else + { + nElements = 0; + } + + TypeDescr td( type ); + typelib_TypeDescriptionReference * element_type = + reinterpret_cast<typelib_IndirectTypeDescription *>(td.get())->pType; + + switch (element_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + jo_ar.reset( jni->NewCharArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetCharArrayRegion( + static_cast<jcharArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jchar *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_BOOLEAN: + jo_ar.reset( jni->NewBooleanArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetBooleanArrayRegion( + static_cast<jbooleanArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jboolean *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_BYTE: + jo_ar.reset( jni->NewByteArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetByteArrayRegion( + static_cast<jbyteArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jbyte *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + jo_ar.reset( jni->NewShortArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetShortArrayRegion( + static_cast<jshortArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jshort *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + jo_ar.reset( jni->NewIntArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetIntArrayRegion( + static_cast<jintArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jint *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + jo_ar.reset( jni->NewLongArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetLongArrayRegion( + static_cast<jlongArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jlong *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_FLOAT: + jo_ar.reset( jni->NewFloatArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetFloatArrayRegion( + static_cast<jfloatArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jfloat *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_DOUBLE: + jo_ar.reset( jni->NewDoubleArray( nElements ) ); + jni.ensure_no_exception(); + if (0 < nElements) + { + jni->SetDoubleArrayRegion( + static_cast<jdoubleArray>(jo_ar.get()), + 0, nElements, reinterpret_cast<jdouble *>(seq->elements) ); + jni.ensure_no_exception(); + } + break; + case typelib_TypeClass_STRING: + jo_ar.reset( + jni->NewObjectArray( + nElements, getJniInfo()->m_class_String, nullptr ) ); + jni.ensure_no_exception(); + if (in_param) + { + rtl_uString * const * pp = + reinterpret_cast<rtl_uString * const *>(seq->elements); + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + JLocalAutoRef jo_string( + jni, ustring_to_jstring( jni, pp[ nPos ] ) ); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), nPos, jo_string.get() ); + jni.ensure_no_exception(); + } + } + break; + case typelib_TypeClass_TYPE: + jo_ar.reset( + jni->NewObjectArray( nElements, getJniInfo()->m_class_Type, nullptr ) ); + jni.ensure_no_exception(); + if (in_param) + { + typelib_TypeDescriptionReference * const * pp = + reinterpret_cast<typelib_TypeDescriptionReference * const *>(seq->elements); + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + jvalue val; + map_to_java( + jni, &val, &pp[ nPos ], element_type, nullptr, + true /* in */, false /* no out */ ); + JLocalAutoRef jo_element( jni, val.l ); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), nPos, jo_element.get() ); + jni.ensure_no_exception(); + } + } + break; + case typelib_TypeClass_ANY: + jo_ar.reset( + jni->NewObjectArray( + nElements, getJniInfo()->m_class_Object, nullptr ) ); + jni.ensure_no_exception(); + if (in_param) + { + uno_Any const * p = reinterpret_cast<uno_Any const *>(seq->elements); + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + jvalue val; + map_to_java( + jni, &val, &p[ nPos ], element_type, nullptr, + true /* in */, false /* no out */ ); + JLocalAutoRef jo_element( jni, val.l ); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), nPos, jo_element.get() ); + jni.ensure_no_exception(); + } + } + break; + case typelib_TypeClass_ENUM: + { + OUString const & element_type_name = + OUString::unacquired( &element_type->pTypeName ); + OString class_name( + OUStringToOString( + element_type_name, RTL_TEXTENCODING_JAVA_UTF8 ) ); + JLocalAutoRef jo_enum_class( + jni, find_class( jni, class_name.getStr() ) ); + + jo_ar.reset( + jni->NewObjectArray( + nElements, static_cast<jclass>(jo_enum_class.get()), nullptr ) ); + jni.ensure_no_exception(); + + if (0 < nElements) + { + // call static <enum_class>.fromInt( int ) + OString sig = "(I)L" + class_name.replace( '.', '/' ) + ";"; + jmethodID method_id = jni->GetStaticMethodID( + static_cast<jclass>(jo_enum_class.get()), "fromInt", sig.getStr() ); + jni.ensure_no_exception(); + assert( method_id != nullptr ); + + sal_Int32 const * p = reinterpret_cast<sal_Int32 const *>(seq->elements); + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + jvalue arg; + arg.i = p[ nPos ]; + JLocalAutoRef jo_enum( + jni, jni->CallStaticObjectMethodA( + static_cast<jclass>(jo_enum_class.get()), method_id, &arg ) ); + jni.ensure_no_exception(); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), nPos, jo_enum.get() ); + jni.ensure_no_exception(); + } + } + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + JNI_type_info const * element_info = + getJniInfo()->get_type_info( jni, element_type ); + + jo_ar.reset( + jni->NewObjectArray( nElements, element_info->m_class, nullptr ) ); + jni.ensure_no_exception(); + + if (0 < nElements) + { + char * p = const_cast<char *>(seq->elements); + sal_Int32 nSize = element_info->m_td.get()->nSize; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + jvalue val; + map_to_java( + jni, &val, p + (nSize * nPos), + element_type, element_info, + true /* in */, false /* no out */ ); + JLocalAutoRef jo_element( jni, val.l ); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), nPos, jo_element.get() ); + jni.ensure_no_exception(); + } + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + OStringBuffer buf( 64 ); + JNI_info::append_sig( + &buf, element_type, false /* use class XInterface */, + false /* '.' instead of '/' */ ); + OString class_name( buf.makeStringAndClear() ); + JLocalAutoRef jo_seq_class( + jni, find_class( jni, class_name.getStr() ) ); + + jo_ar.reset( + jni->NewObjectArray( + nElements, static_cast<jclass>(jo_seq_class.get()), nullptr ) ); + jni.ensure_no_exception(); + + if (0 < nElements) + { + uno_Sequence * const * elements = reinterpret_cast<uno_Sequence * const *>(seq->elements); + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + jvalue java_data2; + map_to_java( + jni, &java_data2, elements + nPos, element_type, nullptr, + true /* in */, false /* no out */ ); + JLocalAutoRef jo_seq( jni, java_data2.l ); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), nPos, jo_seq.get() ); + jni.ensure_no_exception(); + } + } + break; + } + case typelib_TypeClass_INTERFACE: + { + JNI_interface_type_info const * iface_info = + static_cast< JNI_interface_type_info const * >( + getJniInfo()->get_type_info( jni, element_type ) ); + + jo_ar.reset( + jni->NewObjectArray( nElements, iface_info->m_class, nullptr ) ); + jni.ensure_no_exception(); + + if (0 < nElements) + { + uno_Interface * const * pp = reinterpret_cast<uno_Interface * const *>(seq->elements); + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + uno_Interface * pUnoI = pp[ nPos ]; + if (pUnoI != nullptr) + { + JLocalAutoRef jo_element( + jni, map_to_java( jni, pUnoI, iface_info ) ); + jni->SetObjectArrayElement( + static_cast<jobjectArray>(jo_ar.get()), + nPos, jo_element.get() ); + jni.ensure_no_exception(); + } + } + } + break; + } + default: + { + throw BridgeRuntimeError( + "[map_to_java():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported element type: " + + OUString::unacquired( &element_type->pTypeName ) + + jni.get_stack_trace() ); + } + } + + if (out_param) + { + if (java_data->l == nullptr) + { + JLocalAutoRef jo_element_class( + jni, jni->GetObjectClass( jo_ar.get() ) ); + if (in_param) + { + java_data->l = jni->NewObjectArray( + 1, static_cast<jclass>(jo_element_class.get()), jo_ar.get() ); + } + else + { + java_data->l = jni->NewObjectArray( + 1, static_cast<jclass>(jo_element_class.get()), nullptr ); + } + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_ar.get() ); + jni.ensure_no_exception(); + } + } + else + { + java_data->l = jo_ar.release(); + } + break; + } + case typelib_TypeClass_INTERFACE: + { + JLocalAutoRef jo_iface( jni ); + if (in_param) + { + uno_Interface * pUnoI = *static_cast<uno_Interface * const *>(uno_data); + if (pUnoI != nullptr) + { + if (info == nullptr) + info = getJniInfo()->get_type_info( jni, type ); + JNI_interface_type_info const * iface_info = + static_cast< JNI_interface_type_info const * >( info ); + jo_iface.reset( map_to_java( jni, pUnoI, iface_info ) ); + } + } + if (out_param) + { + if (java_data->l == nullptr) + { + if (info == nullptr) + info = getJniInfo()->get_type_info( jni, type ); + java_data->l = + jni->NewObjectArray( 1, info->m_class, jo_iface.get() ); + jni.ensure_no_exception(); + } + else + { + jni->SetObjectArrayElement( + static_cast<jobjectArray>(java_data->l), 0, jo_iface.get() ); + jni.ensure_no_exception(); + } + } + else + { + java_data->l = jo_iface.release(); + } + break; + } + default: + { + throw BridgeRuntimeError( + "[map_to_java():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported type!" + jni.get_stack_trace() ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/jni_uno/jni_helper.h b/bridges/source/jni_uno/jni_helper.h new file mode 100644 index 000000000..a31e9386d --- /dev/null +++ b/bridges/source/jni_uno/jni_helper.h @@ -0,0 +1,153 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_HELPER_H +#define INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_HELPER_H + +#include <sal/config.h> + +#include <memory> + +#include "jni_base.h" +#include "jni_info.h" + + +namespace jni_uno +{ + +inline void jstring_to_ustring( + JNI_context const & jni, rtl_uString ** out_ustr, jstring jstr ) +{ + if (nullptr == jstr) + { + rtl_uString_new( out_ustr ); + } + else + { + jsize len = jni->GetStringLength( jstr ); + std::unique_ptr< rtl_mem > mem( + rtl_mem::allocate( + sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) ); + rtl_uString * ustr = reinterpret_cast<rtl_uString *>(mem.get()); + jni->GetStringRegion( jstr, 0, len, reinterpret_cast<jchar *>(ustr->buffer) ); + jni.ensure_no_exception(); + ustr->refCount = 1; + ustr->length = len; + ustr->buffer[ len ] = '\0'; + mem.release(); + if (nullptr != *out_ustr) + rtl_uString_release( *out_ustr ); + *out_ustr = ustr; + } +} + +inline OUString jstring_to_oustring( + JNI_context const & jni, jstring jstr ) +{ + rtl_uString * ustr = nullptr; + jstring_to_ustring( jni, &ustr, jstr ); + return OUString( ustr, SAL_NO_ACQUIRE ); +} + +inline jstring ustring_to_jstring( + JNI_context const & jni, rtl_uString const * ustr ) +{ + jstring jstr = jni->NewString( reinterpret_cast<jchar const *>(ustr->buffer), ustr->length ); + jni.ensure_no_exception(); + return jstr; +} + + +// if inException, does not handle exceptions, in which case returned value will +// be null if exception occurred: +inline jclass find_class( + JNI_context const & jni, char const * class_name, bool inException = false ) +{ + // find_class may be called before the JNI_info is set: + jclass c=nullptr; + jmethodID m; + JNI_info const * info = jni.get_info(); + if (info == nullptr) { + jni.getClassForName(&c, &m); + if (c == nullptr) { + if (inException) { + return nullptr; + } + jni.ensure_no_exception(); + } + } else { + c = info->m_class_Class; + m = info->m_method_Class_forName; + } + return jni.findClass(class_name, c, m, inException); +} + + +inline jobject create_type( JNI_context const & jni, jclass clazz ) +{ + JNI_info const * jni_info = jni.get_info(); + jvalue arg; + arg.l = clazz; + jobject jo_type = jni->NewObjectA( + jni_info->m_class_Type, jni_info->m_ctor_Type_with_Class, &arg ); + jni.ensure_no_exception(); + return jo_type; +} + +inline jobject create_type( + JNI_context const & jni, typelib_TypeDescriptionReference * type ) +{ + JNI_info const * jni_info = jni.get_info(); + jvalue args[ 2 ]; + // get type class + args[ 0 ].i = type->eTypeClass; + JLocalAutoRef jo_type_class( + jni, jni->CallStaticObjectMethodA( + jni_info->m_class_TypeClass, + jni_info->m_method_TypeClass_fromInt, args ) ); + jni.ensure_no_exception(); + // construct type + JLocalAutoRef jo_type_name( + jni, ustring_to_jstring( jni, type->pTypeName ) ); + args[ 0 ].l = jo_type_name.get(); + args[ 1 ].l = jo_type_class.get(); + jobject jo_type = jni->NewObjectA( + jni_info->m_class_Type, + jni_info->m_ctor_Type_with_Name_TypeClass, args ); + jni.ensure_no_exception(); + return jo_type; +} + +inline jobject compute_oid( JNI_context const & jni, jobject jo ) +{ + JNI_info const * jni_info = jni.get_info(); + jvalue arg; + arg.l= jo; + jobject jo_oid = jni->CallStaticObjectMethodA( + jni_info->m_class_UnoRuntime, + jni_info->m_method_UnoRuntime_generateOid, &arg ); + jni.ensure_no_exception(); + return jo_oid; +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/jni_uno/jni_info.cxx b/bridges/source/jni_uno/jni_info.cxx new file mode 100644 index 000000000..70839e168 --- /dev/null +++ b/bridges/source/jni_uno/jni_info.cxx @@ -0,0 +1,992 @@ +/* -*- 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 <cassert> + +#include "jni_bridge.h" + +#include <com/sun/star/uno/RuntimeException.hpp> + +#include <jvmaccess/unovirtualmachine.hxx> +#include <rtl/string.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> + +#include <uno/lbnames.h> + + +using namespace ::std; +using namespace ::osl; + +namespace jni_uno +{ + + +JNI_type_info::JNI_type_info( + JNI_context const & jni, typelib_TypeDescription * td ) + : m_td( td ), + m_class( nullptr ) +{ + m_td.makeComplete(); + if (! m_td.get()->bComplete) + { + throw BridgeRuntimeError( + "cannot make type complete: " + + OUString::unacquired( &m_td.get()->pTypeName ) + + jni.get_stack_trace() ); + } +} + + +void JNI_interface_type_info::destroy( JNIEnv * jni_env ) +{ + JNI_type_info::destruct( jni_env ); + jni_env->DeleteGlobalRef( m_proxy_ctor ); + jni_env->DeleteGlobalRef( m_type ); + m_methods.reset(); + delete this; +} + + +JNI_interface_type_info::JNI_interface_type_info( + JNI_context const & jni, typelib_TypeDescription * td_ ) + : JNI_type_info( jni, td_ ) +{ + assert( m_td.get()->eTypeClass == typelib_TypeClass_INTERFACE ); + + OUString const & uno_name = OUString::unacquired( &m_td.get()->pTypeName ); + JNI_info const * jni_info = jni.get_info(); + + JLocalAutoRef jo_class( + jni, + find_class( + jni, + ( OUStringToOString( uno_name, RTL_TEXTENCODING_JAVA_UTF8 ). + getStr() ) ) ); + JLocalAutoRef jo_type( jni, create_type( jni, static_cast<jclass>(jo_class.get()) ) ); + + // get proxy ctor + jvalue arg; + arg.l = jo_class.get(); + JLocalAutoRef jo_proxy_ctor( + jni, jni->CallStaticObjectMethodA( + jni_info->m_class_JNI_proxy, + jni_info->m_method_JNI_proxy_get_proxy_ctor, &arg ) ); + + if (is_XInterface( m_td.get()->pWeakRef )) + { + m_methods = nullptr; // no methods + } + else + { + // retrieve method ids for all direct members + try + { + typelib_InterfaceTypeDescription * td = + reinterpret_cast< typelib_InterfaceTypeDescription * >( + m_td.get() ); + // coverity[ctor_dtor_leak] - on purpose + m_methods.reset(new jmethodID[ td->nMapFunctionIndexToMemberIndex ]); + sal_Int32 nMethodIndex = 0; + typelib_TypeDescriptionReference ** ppMembers = td->ppMembers; + sal_Int32 nMembers = td->nMembers; + + for ( sal_Int32 nPos = 0; nPos < nMembers; ++nPos ) + { + TypeDescr member_td( ppMembers[ nPos ] ); + + OStringBuffer sig_buf( 64 ); + + if (member_td.get()->eTypeClass == + typelib_TypeClass_INTERFACE_METHOD) // method + { + typelib_InterfaceMethodTypeDescription * method_td = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + member_td.get() ); + + sig_buf.append( '(' ); + for ( sal_Int32 i = 0; i < method_td->nParams; ++i ) + { + typelib_MethodParameter const & param = + method_td->pParams[ i ]; + if (param.bOut) + sig_buf.append( '[' ); + JNI_info::append_sig( &sig_buf, param.pTypeRef ); + } + sig_buf.append( ')' ); + JNI_info::append_sig( &sig_buf, method_td->pReturnTypeRef ); + + OString method_signature( sig_buf.makeStringAndClear() ); + OString method_name( + OUStringToOString( OUString::unacquired( + &method_td->aBase.pMemberName ), + RTL_TEXTENCODING_JAVA_UTF8 ) ); + + m_methods[ nMethodIndex ] = jni->GetMethodID( + static_cast<jclass>(jo_class.get()), method_name.getStr(), + method_signature.getStr() ); + jni.ensure_no_exception(); + assert( m_methods[ nMethodIndex ] != nullptr ); + ++nMethodIndex; + } + else // attribute + { + assert( + member_td.get()->eTypeClass == + typelib_TypeClass_INTERFACE_ATTRIBUTE ); + typelib_InterfaceAttributeTypeDescription * attribute_td = + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + member_td.get() ); + + // type sig + JNI_info::append_sig( + &sig_buf, attribute_td->pAttributeTypeRef ); + OString type_sig( sig_buf.makeStringAndClear() ); + sig_buf.ensureCapacity( 64 ); + // member name + OUString const & member_name = + OUString::unacquired( + &attribute_td->aBase.pMemberName ); + + // getter + sig_buf.append( "()" ); + sig_buf.append( type_sig ); + OString method_signature( sig_buf.makeStringAndClear() ); + OUStringBuffer name_buf( 3 + member_name.getLength() ); + name_buf.append( "get" ); + name_buf.append( member_name ); + OString method_name( + OUStringToOString( + name_buf.makeStringAndClear(), + RTL_TEXTENCODING_JAVA_UTF8 ) ); + m_methods[ nMethodIndex ] = jni->GetMethodID( + static_cast<jclass>(jo_class.get()), method_name.getStr(), + method_signature.getStr() ); + jni.ensure_no_exception(); + assert( m_methods[ nMethodIndex ] != nullptr ); + ++nMethodIndex; + if (! attribute_td->bReadOnly) + { + // setter + sig_buf.ensureCapacity( 64 ); + sig_buf.append( '(' ); + sig_buf.append( type_sig ); + sig_buf.append( ")V" ); + method_signature = sig_buf.makeStringAndClear(); + name_buf.ensureCapacity( 3 + member_name.getLength() ); + name_buf.append( "set" ); + name_buf.append( member_name ); + method_name = OUStringToOString( + name_buf.makeStringAndClear(), + RTL_TEXTENCODING_JAVA_UTF8 ); + m_methods[ nMethodIndex ] = jni->GetMethodID( + static_cast<jclass>(jo_class.get()), method_name.getStr(), + method_signature.getStr() ); + jni.ensure_no_exception(); + assert( m_methods[ nMethodIndex ] != nullptr ); + ++nMethodIndex; + } + } + } + } + catch (...) + { + m_methods.reset(); + throw; + } + } + m_class = static_cast<jclass>(jni->NewGlobalRef( jo_class.get() )); + m_type = jni->NewGlobalRef( jo_type.get() ); + m_proxy_ctor = jni->NewGlobalRef( jo_proxy_ctor.get() ); +} + + +void JNI_compound_type_info::destroy( JNIEnv * jni_env ) +{ + JNI_type_info::destruct( jni_env ); + m_fields.reset(); + delete this; +} + + +JNI_compound_type_info::JNI_compound_type_info( + JNI_context const & jni, typelib_TypeDescription * td_ ) + : JNI_type_info( jni, td_ ), + m_exc_ctor( nullptr ) +{ + assert( m_td.get()->eTypeClass == typelib_TypeClass_STRUCT || + m_td.get()->eTypeClass == typelib_TypeClass_EXCEPTION ); + typelib_CompoundTypeDescription * td = + reinterpret_cast< typelib_CompoundTypeDescription * >( m_td.get() ); + + OUString const & uno_name = + OUString::unacquired( &td->aBase.pTypeName ); + + // Erase type arguments of instantiated polymorphic struct types: + OUString nucleus; + sal_Int32 i = uno_name.indexOf( '<' ); + if ( i < 0 ) { + nucleus = uno_name; + } else { + nucleus = uno_name.copy( 0, i ); + } + JLocalAutoRef jo_class( + jni, + find_class( + jni, + OUStringToOString( + nucleus, RTL_TEXTENCODING_JAVA_UTF8 ).getStr() ) ); + + JNI_info const * jni_info = jni.get_info(); + + if (m_td.get()->eTypeClass == typelib_TypeClass_EXCEPTION) + { + // retrieve exc ctor( msg ) + m_exc_ctor = jni->GetMethodID( + static_cast<jclass>(jo_class.get()), "<init>", "(Ljava/lang/String;)V" ); + jni.ensure_no_exception(); + assert( m_exc_ctor != nullptr ); + } + + // retrieve info for base type + typelib_TypeDescription * base_td = + type_equals( + td->aBase.pWeakRef, + jni_info->m_RuntimeException_type.getTypeLibType()) + ? nullptr + : reinterpret_cast< typelib_TypeDescription * >( + td->pBaseTypeDescription ); + m_base = (base_td == nullptr ? nullptr : jni_info->get_type_info( jni, base_td )); + + try + { + if (type_equals( + td->aBase.pWeakRef, + jni_info->m_Exception_type.getTypeLibType() ) || + type_equals( + td->aBase.pWeakRef, + jni_info->m_RuntimeException_type.getTypeLibType() )) + { + // coverity[ctor_dtor_leak] - on purpose + m_fields.reset(new jfieldID[ 2 ]); + m_fields[ 0 ] = nullptr; // special Throwable.getMessage() + // field Context + m_fields[ 1 ] = jni->GetFieldID( + static_cast<jclass>(jo_class.get()), "Context", "Ljava/lang/Object;" ); + jni.ensure_no_exception(); + assert( m_fields[ 1 ] != nullptr ); + } + else + { + // retrieve field ids for all direct members + sal_Int32 nMembers = td->nMembers; + m_fields.reset(new jfieldID[ nMembers ]); + + for ( sal_Int32 nPos = 0; nPos < nMembers; ++nPos ) + { + OString sig; + if (td->aBase.eTypeClass == typelib_TypeClass_STRUCT + && reinterpret_cast< typelib_StructTypeDescription * >( + td)->pParameterizedTypes != nullptr + && reinterpret_cast< typelib_StructTypeDescription * >( + td)->pParameterizedTypes[nPos]) + { + sig = OString( "Ljava/lang/Object;" ); + } else { + OStringBuffer sig_buf( 32 ); + JNI_info::append_sig( &sig_buf, td->ppTypeRefs[ nPos ] ); + sig = sig_buf.makeStringAndClear(); + } + + OString member_name( + OUStringToOString( + OUString::unacquired( &td->ppMemberNames[ nPos ] ), + RTL_TEXTENCODING_JAVA_UTF8 ) ); + + m_fields[ nPos ] = jni->GetFieldID( + static_cast<jclass>(jo_class.get()), member_name.getStr(), + sig.getStr() ); + jni.ensure_no_exception(); + assert( m_fields[ nPos ] != nullptr ); + } + } + } + catch (...) + { + m_fields.reset(); + throw; + } + + m_class = static_cast<jclass>(jni->NewGlobalRef( jo_class.get() )); +} + + +JNI_type_info const * JNI_info::create_type_info( + JNI_context const & jni, typelib_TypeDescription * td ) const +{ + OUString const & uno_name = OUString::unacquired( &td->pTypeName ); + + JNI_type_info * new_info; + switch (td->eTypeClass) + { + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + new_info = new JNI_compound_type_info( jni, td ); + break; + } + case typelib_TypeClass_INTERFACE: + { + new_info = new JNI_interface_type_info( jni, td ); + break; + } + default: + { + throw BridgeRuntimeError( + "type info not supported for " + uno_name + jni.get_stack_trace() ); + } + } + + // look up + JNI_type_info * info; + ClearableMutexGuard guard( m_mutex ); + JNI_type_info_holder & holder = m_type_map[ uno_name ]; + if (holder.m_info == nullptr) // new insertion + { + holder.m_info = new_info; + guard.clear(); + info = new_info; + } + else // inserted in the meantime + { + info = holder.m_info; + guard.clear(); + new_info->destroy( jni.get_jni_env() ); + } + return info; +} + + +JNI_type_info const * JNI_info::get_type_info( + JNI_context const & jni, typelib_TypeDescription * td ) const +{ + if (is_XInterface( td->pWeakRef )) + { + return m_XInterface_type_info; + } + + OUString const & uno_name = OUString::unacquired( &td->pTypeName ); + JNI_type_info const * info; + ClearableMutexGuard guard( m_mutex ); + + t_str2type::const_iterator iFind( m_type_map.find( uno_name ) ); + if (iFind == m_type_map.end()) + { + guard.clear(); + info = create_type_info( jni, td ); + } + else + { + info = iFind->second.m_info; + } + + return info; +} + + +JNI_type_info const * JNI_info::get_type_info( + JNI_context const & jni, typelib_TypeDescriptionReference * type ) const +{ + if (is_XInterface( type )) + { + return m_XInterface_type_info; + } + + OUString const & uno_name = OUString::unacquired( &type->pTypeName ); + JNI_type_info const * info; + ClearableMutexGuard guard( m_mutex ); + t_str2type::const_iterator iFind( m_type_map.find( uno_name ) ); + if (iFind == m_type_map.end()) + { + guard.clear(); + TypeDescr td( type ); + info = create_type_info( jni, td.get() ); + } + else + { + info = iFind->second.m_info; + } + + return info; +} + + +JNI_type_info const * JNI_info::get_type_info( + JNI_context const & jni, OUString const & uno_name ) const +{ + if ( uno_name == "com.sun.star.uno.XInterface" ) + { + return m_XInterface_type_info; + } + + JNI_type_info const * info; + ClearableMutexGuard guard( m_mutex ); + t_str2type::const_iterator iFind( m_type_map.find( uno_name ) ); + if (iFind == m_type_map.end()) + { + guard.clear(); + css::uno::TypeDescription td( uno_name ); + if (! td.is()) + { + throw BridgeRuntimeError( + "UNO type not found: " + uno_name + jni.get_stack_trace() ); + } + info = create_type_info( jni, td.get() ); + } + else + { + info = iFind->second.m_info; + } + + return info; +} + + +JNI_info::JNI_info( + JNIEnv * jni_env, jobject class_loader, jclass classClass, + jmethodID methodForName ) + : m_class_Class( classClass ), + m_method_Class_forName( methodForName ), + m_class_JNI_proxy( nullptr ), + m_XInterface_queryInterface_td( + (reinterpret_cast< typelib_InterfaceTypeDescription * >( + css::uno::TypeDescription( + cppu::UnoType<css::uno::XInterface>::get()) + .get())->ppMembers[ 0 ] ) ), + m_Exception_type(cppu::UnoType<css::uno::Exception>::get()), + m_RuntimeException_type(cppu::UnoType<css::uno::RuntimeException>::get()), + m_void_type(cppu::UnoType<void>::get()), + m_XInterface_type_info( nullptr ) +{ + JNI_context jni( this, jni_env, class_loader ); // !no proper jni_info! + + // class lookup + JLocalAutoRef jo_Object( + jni, find_class( jni, "java.lang.Object" ) ); + JLocalAutoRef jo_Class( + jni, find_class( jni, "java.lang.Class" ) ); + JLocalAutoRef jo_Throwable( + jni, find_class( jni, "java.lang.Throwable" ) ); + JLocalAutoRef jo_Character( + jni, find_class( jni, "java.lang.Character" ) ); + JLocalAutoRef jo_Boolean( + jni, find_class( jni, "java.lang.Boolean" ) ); + JLocalAutoRef jo_Byte( + jni, find_class( jni, "java.lang.Byte" ) ); + JLocalAutoRef jo_Short( + jni, find_class( jni, "java.lang.Short" ) ); + JLocalAutoRef jo_Integer( + jni, find_class( jni, "java.lang.Integer" ) ); + JLocalAutoRef jo_Long( + jni, find_class( jni, "java.lang.Long" ) ); + JLocalAutoRef jo_Float( + jni, find_class( jni, "java.lang.Float" ) ); + JLocalAutoRef jo_Double( + jni, find_class( jni, "java.lang.Double" ) ); + JLocalAutoRef jo_String( + jni, find_class( jni, "java.lang.String" ) ); + JLocalAutoRef jo_RuntimeException( + jni, find_class( jni, "com.sun.star.uno.RuntimeException" ) ); + JLocalAutoRef jo_UnoRuntime( + jni, find_class( jni, "com.sun.star.uno.UnoRuntime" ) ); + JLocalAutoRef jo_Any( + jni, find_class( jni, "com.sun.star.uno.Any" ) ); + JLocalAutoRef jo_Enum( + jni, find_class( jni, "com.sun.star.uno.Enum" ) ); + JLocalAutoRef jo_Type( + jni, find_class( jni, "com.sun.star.uno.Type" ) ); + JLocalAutoRef jo_TypeClass( + jni, find_class( jni, "com.sun.star.uno.TypeClass" ) ); + JLocalAutoRef jo_IEnvironment( + jni, find_class( jni, "com.sun.star.uno.IEnvironment" ) ); + JLocalAutoRef jo_JNI_proxy( + jni, find_class( jni, "com.sun.star.bridges.jni_uno.JNI_proxy" ) ); + JLocalAutoRef jo_AsynchronousFinalizer( + jni, find_class( jni, "com.sun.star.lib.util.AsynchronousFinalizer" ) ); + + // method Object.toString() + m_method_Object_toString = jni->GetMethodID( + static_cast<jclass>(jo_Object.get()), "toString", "()Ljava/lang/String;" ); + jni.ensure_no_exception(); + assert( m_method_Object_toString != nullptr ); + // method Class.getName() + m_method_Class_getName = jni->GetMethodID( + static_cast<jclass>(jo_Class.get()), "getName", "()Ljava/lang/String;" ); + jni.ensure_no_exception(); + assert( m_method_Class_getName != nullptr ); + + // method Throwable.getMessage() + m_method_Throwable_getMessage = jni->GetMethodID( + static_cast<jclass>(jo_Throwable.get()), "getMessage", "()Ljava/lang/String;" ); + jni.ensure_no_exception(); + assert( m_method_Throwable_getMessage != nullptr ); + + // method Character.charValue() + m_method_Character_charValue = jni->GetMethodID( + static_cast<jclass>(jo_Character.get()), "charValue", "()C" ); + jni.ensure_no_exception(); + assert( m_method_Character_charValue != nullptr ); + // method Boolean.booleanValue() + m_method_Boolean_booleanValue = jni->GetMethodID( + static_cast<jclass>(jo_Boolean.get()), "booleanValue", "()Z" ); + jni.ensure_no_exception(); + assert( m_method_Boolean_booleanValue != nullptr ); + // method Byte.byteValue() + m_method_Byte_byteValue = jni->GetMethodID( + static_cast<jclass>(jo_Byte.get()), "byteValue", "()B" ); + jni.ensure_no_exception(); + assert( m_method_Byte_byteValue != nullptr ); + // method Short.shortValue() + m_method_Short_shortValue = jni->GetMethodID( + static_cast<jclass>(jo_Short.get()), "shortValue", "()S" ); + jni.ensure_no_exception(); + assert( m_method_Short_shortValue != nullptr ); + // method Integer.intValue() + m_method_Integer_intValue = jni->GetMethodID( + static_cast<jclass>(jo_Integer.get()), "intValue", "()I" ); + jni.ensure_no_exception(); + assert( m_method_Integer_intValue != nullptr ); + // method Long.longValue() + m_method_Long_longValue = jni->GetMethodID( + static_cast<jclass>(jo_Long.get()), "longValue", "()J" ); + jni.ensure_no_exception(); + assert( m_method_Long_longValue != nullptr ); + // method Float.floatValue() + m_method_Float_floatValue = jni->GetMethodID( + static_cast<jclass>(jo_Float.get()), "floatValue", "()F" ); + jni.ensure_no_exception(); + assert( m_method_Float_floatValue != nullptr ); + // method Double.doubleValue() + m_method_Double_doubleValue = jni->GetMethodID( + static_cast<jclass>(jo_Double.get()), "doubleValue", "()D" ); + jni.ensure_no_exception(); + assert( m_method_Double_doubleValue != nullptr ); + + // ctor Character( char ) + m_ctor_Character_with_char = jni->GetMethodID( + static_cast<jclass>(jo_Character.get()), "<init>", "(C)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Character_with_char != nullptr ); + // ctor Boolean( boolean ) + m_ctor_Boolean_with_boolean = jni->GetMethodID( + static_cast<jclass>(jo_Boolean.get()), "<init>", "(Z)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Boolean_with_boolean != nullptr ); + // ctor Byte( byte ) + m_ctor_Byte_with_byte = jni->GetMethodID( + static_cast<jclass>(jo_Byte.get()), "<init>", "(B)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Byte_with_byte != nullptr ); + // ctor Short( short ) + m_ctor_Short_with_short = jni->GetMethodID( + static_cast<jclass>(jo_Short.get()), "<init>", "(S)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Short_with_short != nullptr ); + // ctor Integer( int ) + m_ctor_Integer_with_int = jni->GetMethodID( + static_cast<jclass>(jo_Integer.get()), "<init>", "(I)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Integer_with_int != nullptr ); + // ctor Long( long ) + m_ctor_Long_with_long = jni->GetMethodID( + static_cast<jclass>(jo_Long.get()), "<init>", "(J)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Long_with_long != nullptr ); + // ctor Float( float ) + m_ctor_Float_with_float = jni->GetMethodID( + static_cast<jclass>(jo_Float.get()), "<init>", "(F)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Float_with_float != nullptr ); + // ctor Double( double ) + m_ctor_Double_with_double = jni->GetMethodID( + static_cast<jclass>(jo_Double.get()), "<init>", "(D)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Double_with_double != nullptr ); + + // static method UnoRuntime.generateOid() + m_method_UnoRuntime_generateOid = jni->GetStaticMethodID( + static_cast<jclass>(jo_UnoRuntime.get()), + "generateOid", "(Ljava/lang/Object;)Ljava/lang/String;" ); + jni.ensure_no_exception(); + assert( m_method_UnoRuntime_generateOid != nullptr ); + // static method UnoRuntime.queryInterface() + m_method_UnoRuntime_queryInterface = jni->GetStaticMethodID( + static_cast<jclass>(jo_UnoRuntime.get()), + "queryInterface", + "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)Ljava/lang/Object;" ); + jni.ensure_no_exception(); + assert( m_method_UnoRuntime_queryInterface != nullptr ); + + // field Enum.m_value + m_field_Enum_m_value = jni->GetFieldID( + static_cast<jclass>(jo_Enum.get()), "m_value", "I" ); + jni.ensure_no_exception(); + assert( m_field_Enum_m_value != nullptr ); + + // static method TypeClass.fromInt() + m_method_TypeClass_fromInt = jni->GetStaticMethodID( + static_cast<jclass>(jo_TypeClass.get()), + "fromInt", "(I)Lcom/sun/star/uno/TypeClass;" ); + jni.ensure_no_exception(); + assert( m_method_TypeClass_fromInt != nullptr ); + + // ctor Type( Class ) + m_ctor_Type_with_Class = jni->GetMethodID( + static_cast<jclass>(jo_Type.get()), "<init>", "(Ljava/lang/Class;)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Type_with_Class != nullptr ); + // ctor Type( String, TypeClass ) + m_ctor_Type_with_Name_TypeClass = jni->GetMethodID( + static_cast<jclass>(jo_Type.get()), + "<init>", "(Ljava/lang/String;Lcom/sun/star/uno/TypeClass;)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Type_with_Name_TypeClass != nullptr ); + // field Type._typeName + m_field_Type_typeName = jni->GetFieldID( + static_cast<jclass>(jo_Type.get()), "_typeName", "Ljava/lang/String;" ); + jni.ensure_no_exception(); + assert( m_field_Type_typeName != nullptr ); + + // ctor Any( Type, Object ) + m_ctor_Any_with_Type_Object = jni->GetMethodID( + static_cast<jclass>(jo_Any.get()), + "<init>", "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V" ); + jni.ensure_no_exception(); + assert( m_ctor_Any_with_Type_Object != nullptr ); + + // field Any._type + m_field_Any_type = jni->GetFieldID( + static_cast<jclass>(jo_Any.get()), "_type", "Lcom/sun/star/uno/Type;" ); + jni.ensure_no_exception(); + assert( m_field_Any_type != nullptr ); + // field Any._object + m_field_Any_object = jni->GetFieldID( + static_cast<jclass>(jo_Any.get()), "_object", "Ljava/lang/Object;" ); + jni.ensure_no_exception(); + assert( m_field_Any_object != nullptr ); + + // method IEnvironment.getRegisteredInterface() + m_method_IEnvironment_getRegisteredInterface = jni->GetMethodID( + static_cast<jclass>(jo_IEnvironment.get()), + "getRegisteredInterface", + "(Ljava/lang/String;Lcom/sun/star/uno/Type;)Ljava/lang/Object;" ); + jni.ensure_no_exception(); + assert( m_method_IEnvironment_getRegisteredInterface != nullptr ); + // method IEnvironment.registerInterface() + m_method_IEnvironment_registerInterface = jni->GetMethodID( + static_cast<jclass>(jo_IEnvironment.get()), "registerInterface", + "(Ljava/lang/Object;[Ljava/lang/String;Lcom/sun/star/uno/Type;)" + "Ljava/lang/Object;" ); + jni.ensure_no_exception(); + assert( m_method_IEnvironment_registerInterface != nullptr ); + + // static method JNI_proxy.get_proxy_ctor() + m_method_JNI_proxy_get_proxy_ctor = jni->GetStaticMethodID( + static_cast<jclass>(jo_JNI_proxy.get()), "get_proxy_ctor", + "(Ljava/lang/Class;)Ljava/lang/reflect/Constructor;" ); + jni.ensure_no_exception(); + assert( m_method_JNI_proxy_get_proxy_ctor != nullptr ); + // static method JNI_proxy.create() + m_method_JNI_proxy_create = jni->GetStaticMethodID( + static_cast<jclass>(jo_JNI_proxy.get()), "create", + "(JLcom/sun/star/uno/IEnvironment;JJLcom/sun/star/uno/Type;Ljava/lang" + "/String;Ljava/lang/reflect/Constructor;" + "Lcom/sun/star/lib/util/AsynchronousFinalizer;)Ljava/lang/Object;" ); + jni.ensure_no_exception(); + assert( m_method_JNI_proxy_create != nullptr ); + // field JNI_proxy.m_receiver_handle + m_field_JNI_proxy_m_receiver_handle = jni->GetFieldID( + static_cast<jclass>(jo_JNI_proxy.get()), "m_receiver_handle", "J" ); + jni.ensure_no_exception(); + assert( m_field_JNI_proxy_m_receiver_handle != nullptr ); + // field JNI_proxy.m_td_handle + m_field_JNI_proxy_m_td_handle = jni->GetFieldID( + static_cast<jclass>(jo_JNI_proxy.get()), "m_td_handle", "J" ); + jni.ensure_no_exception(); + assert( m_field_JNI_proxy_m_td_handle != nullptr ); + // field JNI_proxy.m_type + m_field_JNI_proxy_m_type = jni->GetFieldID( + static_cast<jclass>(jo_JNI_proxy.get()), "m_type", "Lcom/sun/star/uno/Type;" ); + jni.ensure_no_exception(); + assert( m_field_JNI_proxy_m_type != nullptr ); + // field JNI_proxy.m_oid + m_field_JNI_proxy_m_oid = jni->GetFieldID( + static_cast<jclass>(jo_JNI_proxy.get()), "m_oid", "Ljava/lang/String;" ); + jni.ensure_no_exception(); + assert( m_field_JNI_proxy_m_oid != nullptr ); + + // ctor AsynchronousFinalizer + m_ctor_AsynchronousFinalizer = jni->GetMethodID( + static_cast<jclass>(jo_AsynchronousFinalizer.get()), "<init>", "()V" ); + jni.ensure_no_exception(); + assert( m_ctor_AsynchronousFinalizer != nullptr ); + // method AsynchronousFinalizer.drain() + m_method_AsynchronousFinalizer_drain = jni->GetMethodID( + static_cast<jclass>(jo_AsynchronousFinalizer.get()), "drain", "()V" ); + jni.ensure_no_exception(); + assert( m_method_AsynchronousFinalizer_drain != nullptr ); + + // get java env + OUString java_env_type_name( UNO_LB_JAVA ); + JLocalAutoRef jo_java( + jni, ustring_to_jstring( jni, java_env_type_name.pData ) ); + jvalue args[ 2 ]; + args[ 0 ].l = jo_java.get(); + args[ 1 ].l = nullptr; + jmethodID method_getEnvironment = jni->GetStaticMethodID( + static_cast<jclass>(jo_UnoRuntime.get()), "getEnvironment", + "(Ljava/lang/String;Ljava/lang/Object;)" + "Lcom/sun/star/uno/IEnvironment;" ); + jni.ensure_no_exception(); + assert( method_getEnvironment != nullptr ); + JLocalAutoRef jo_java_env( + jni, jni->CallStaticObjectMethodA( + static_cast<jclass>(jo_UnoRuntime.get()), method_getEnvironment, args ) ); + + // get com.sun.star.uno.Any.VOID + jfieldID field_Any_VOID = jni->GetStaticFieldID( + static_cast<jclass>(jo_Any.get()), "VOID", "Lcom/sun/star/uno/Any;" ); + jni.ensure_no_exception(); + assert( field_Any_VOID != nullptr ); + JLocalAutoRef jo_Any_VOID( + jni, jni->GetStaticObjectField( + static_cast<jclass>(jo_Any.get()), field_Any_VOID ) ); + // get com.sun.star.uno.Type.UNSIGNED_SHORT + jfieldID field_Type_UNSIGNED_SHORT = jni->GetStaticFieldID( + static_cast<jclass>(jo_Type.get()), "UNSIGNED_SHORT", "Lcom/sun/star/uno/Type;" ); + jni.ensure_no_exception(); + assert( field_Type_UNSIGNED_SHORT != nullptr ); + JLocalAutoRef jo_Type_UNSIGNED_SHORT( + jni, jni->GetStaticObjectField( + static_cast<jclass>(jo_Type.get()), field_Type_UNSIGNED_SHORT ) ); + // get com.sun.star.uno.Type.UNSIGNED_LONG + jfieldID field_Type_UNSIGNED_LONG = jni->GetStaticFieldID( + static_cast<jclass>(jo_Type.get()), "UNSIGNED_LONG", "Lcom/sun/star/uno/Type;" ); + jni.ensure_no_exception(); + assert( field_Type_UNSIGNED_LONG != nullptr ); + JLocalAutoRef jo_Type_UNSIGNED_LONG( + jni, jni->GetStaticObjectField( + static_cast<jclass>(jo_Type.get()), field_Type_UNSIGNED_LONG ) ); + // get com.sun.star.uno.Type.UNSIGNED_HYPER + jfieldID field_Type_UNSIGNED_HYPER = jni->GetStaticFieldID( + static_cast<jclass>(jo_Type.get()), "UNSIGNED_HYPER", "Lcom/sun/star/uno/Type;" ); + jni.ensure_no_exception(); + assert( field_Type_UNSIGNED_HYPER != nullptr ); + JLocalAutoRef jo_Type_UNSIGNED_HYPER( + jni, jni->GetStaticObjectField( + static_cast<jclass>(jo_Type.get()), field_Type_UNSIGNED_HYPER ) ); + + // make global refs + m_class_UnoRuntime = + static_cast<jclass>(jni->NewGlobalRef( jo_UnoRuntime.get() )); + m_class_RuntimeException = + static_cast<jclass>(jni->NewGlobalRef( jo_RuntimeException.get() )); + m_class_Any = + static_cast<jclass>(jni->NewGlobalRef( jo_Any.get() )); + m_class_Type = + static_cast<jclass>(jni->NewGlobalRef( jo_Type.get() )); + m_class_TypeClass = + static_cast<jclass>(jni->NewGlobalRef( jo_TypeClass.get() )); + m_class_JNI_proxy = + static_cast<jclass>(jni->NewGlobalRef( jo_JNI_proxy.get() )); + m_class_AsynchronousFinalizer = + static_cast<jclass>(jni->NewGlobalRef( jo_AsynchronousFinalizer.get() )); + + m_class_Character = + static_cast<jclass>(jni->NewGlobalRef( jo_Character.get() )); + m_class_Boolean = + static_cast<jclass>(jni->NewGlobalRef( jo_Boolean.get() )); + m_class_Byte = + static_cast<jclass>(jni->NewGlobalRef( jo_Byte.get() )); + m_class_Short = + static_cast<jclass>(jni->NewGlobalRef( jo_Short.get() )); + m_class_Integer = + static_cast<jclass>(jni->NewGlobalRef( jo_Integer.get() )); + m_class_Long = + static_cast<jclass>(jni->NewGlobalRef( jo_Long.get() )); + m_class_Float = + static_cast<jclass>(jni->NewGlobalRef( jo_Float.get() )); + m_class_Double = + static_cast<jclass>(jni->NewGlobalRef( jo_Double.get() )); + m_class_String = + static_cast<jclass>(jni->NewGlobalRef( jo_String.get() )); + m_class_Object = + static_cast<jclass>(jni->NewGlobalRef( jo_Object.get() )); + m_class_Class = + static_cast<jclass>(jni->NewGlobalRef( m_class_Class )); + + m_object_Any_VOID = + jni->NewGlobalRef( jo_Any_VOID.get() ); + m_object_Type_UNSIGNED_SHORT = + jni->NewGlobalRef( jo_Type_UNSIGNED_SHORT.get() ); + m_object_Type_UNSIGNED_LONG = + jni->NewGlobalRef( jo_Type_UNSIGNED_LONG.get() ); + m_object_Type_UNSIGNED_HYPER = + jni->NewGlobalRef( jo_Type_UNSIGNED_HYPER.get() ); + m_object_java_env = jni->NewGlobalRef( jo_java_env.get() ); + + try + { + css::uno::TypeDescription XInterface_td( + cppu::UnoType<css::uno::XInterface>::get()); + // coverity[ctor_dtor_leak] - on purpose + m_XInterface_type_info = + new JNI_interface_type_info( jni, XInterface_td.get() ); + } + catch (...) + { + destruct( jni_env ); + throw; + } +} + + +void JNI_info::destruct( JNIEnv * jni_env ) +{ + for (auto & i: m_type_map) + { + i.second.m_info->destroy( jni_env ); + } + if (m_XInterface_type_info != nullptr) + { + const_cast< JNI_interface_type_info * >( + m_XInterface_type_info )->destroy( jni_env ); + } + + // free global refs + jni_env->DeleteGlobalRef( m_object_java_env ); + jni_env->DeleteGlobalRef( m_object_Any_VOID ); + jni_env->DeleteGlobalRef( m_object_Type_UNSIGNED_SHORT ); + jni_env->DeleteGlobalRef( m_object_Type_UNSIGNED_LONG ); + jni_env->DeleteGlobalRef( m_object_Type_UNSIGNED_HYPER ); + + jni_env->DeleteGlobalRef( m_class_Class ); + jni_env->DeleteGlobalRef( m_class_Object ); + jni_env->DeleteGlobalRef( m_class_String ); + jni_env->DeleteGlobalRef( m_class_Double ); + jni_env->DeleteGlobalRef( m_class_Float ); + jni_env->DeleteGlobalRef( m_class_Long ); + jni_env->DeleteGlobalRef( m_class_Integer ); + jni_env->DeleteGlobalRef( m_class_Short ); + jni_env->DeleteGlobalRef( m_class_Byte ); + jni_env->DeleteGlobalRef( m_class_Boolean ); + jni_env->DeleteGlobalRef( m_class_Character ); + + jni_env->DeleteGlobalRef( m_class_AsynchronousFinalizer ); + jni_env->DeleteGlobalRef( m_class_JNI_proxy ); + jni_env->DeleteGlobalRef( m_class_RuntimeException ); + jni_env->DeleteGlobalRef( m_class_UnoRuntime ); + jni_env->DeleteGlobalRef( m_class_TypeClass ); + jni_env->DeleteGlobalRef( m_class_Type ); + jni_env->DeleteGlobalRef( m_class_Any ); +} + + +JNI_info const * JNI_info::get_jni_info( + rtl::Reference< jvmaccess::UnoVirtualMachine > const & uno_vm ) +{ + // !!!no JNI_info available at JNI_context!!! + ::jvmaccess::VirtualMachine::AttachGuard guard( + uno_vm->getVirtualMachine() ); + JNIEnv * jni_env = guard.getEnvironment(); + JNI_context jni( + nullptr, jni_env, static_cast< jobject >(uno_vm->getClassLoader()) ); + + jclass jo_class; + jmethodID jo_forName; + jni.getClassForName( &jo_class, &jo_forName ); + jni.ensure_no_exception(); + JLocalAutoRef jo_JNI_info_holder( + jni, + jni.findClass( + "com.sun.star.bridges.jni_uno.JNI_info_holder", jo_class, + jo_forName, false ) ); + // field JNI_info_holder.m_jni_info_handle + jfieldID field_s_jni_info_handle = + jni->GetStaticFieldID( + static_cast<jclass>(jo_JNI_info_holder.get()), "s_jni_info_handle", "J" ); + jni.ensure_no_exception(); + assert( field_s_jni_info_handle != nullptr ); + + JNI_info const * jni_info = + reinterpret_cast< JNI_info const * >( + jni->GetStaticLongField( + static_cast<jclass>(jo_JNI_info_holder.get()), field_s_jni_info_handle ) ); + if (jni_info == nullptr) // un-initialized? + { + JNI_info * new_info = new JNI_info( + jni_env, static_cast< jobject >(uno_vm->getClassLoader()), jo_class, + jo_forName ); + + ClearableMutexGuard g( Mutex::getGlobalMutex() ); + jni_info = + reinterpret_cast< JNI_info const * >( + jni->GetStaticLongField( + static_cast<jclass>(jo_JNI_info_holder.get()), + field_s_jni_info_handle ) ); + if (jni_info == nullptr) // still un-initialized? + { + jni->SetStaticLongField( + static_cast<jclass>(jo_JNI_info_holder.get()), field_s_jni_info_handle, + reinterpret_cast< jlong >( new_info ) ); + jni_info = new_info; + } + else + { + g.clear(); + new_info->destroy( jni_env ); + } + } + + return jni_info; +} + +} + +extern "C" +{ + + +SAL_JNI_EXPORT void +JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1info_1holder_finalize__J( + JNIEnv * jni_env, SAL_UNUSED_PARAMETER jobject, jlong jni_info_handle ) + SAL_THROW_EXTERN_C() +{ + ::jni_uno::JNI_info * jni_info = + reinterpret_cast< ::jni_uno::JNI_info * >( jni_info_handle ); + jni_info->destroy( jni_env ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/jni_uno/jni_info.h b/bridges/source/jni_uno/jni_info.h new file mode 100644 index 000000000..98ba7c9e5 --- /dev/null +++ b/bridges/source/jni_uno/jni_info.h @@ -0,0 +1,369 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_INFO_H +#define INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNI_INFO_H + +#include <sal/config.h> + +#include <unordered_map> + +#include "jni_base.h" + +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <rtl/strbuf.hxx> + +#include <uno/environment.h> +#include <typelib/typedescription.hxx> + +#include <com/sun/star/uno/Type.hxx> + +namespace jvmaccess { class UnoVirtualMachine; } + +namespace jni_uno +{ + +inline bool type_equals( + typelib_TypeDescriptionReference * type1, + typelib_TypeDescriptionReference * type2 ) +{ + if (type1 == type2) + return true; + OUString const & name1 = + OUString::unacquired( &type1->pTypeName ); + OUString const & name2 = + OUString::unacquired( &type2->pTypeName ); + return ((type1->eTypeClass == type2->eTypeClass) && name1 == name2); +} + +inline bool is_XInterface( typelib_TypeDescriptionReference * type ) +{ + return ((typelib_TypeClass_INTERFACE == type->eTypeClass) && + OUString::unacquired( &type->pTypeName ) == "com.sun.star.uno.XInterface"); +} + +struct JNI_type_info +{ + JNI_type_info(const JNI_type_info&) = delete; + const JNI_type_info& operator=(const JNI_type_info&) = delete; + + ::com::sun::star::uno::TypeDescription m_td; + jclass m_class; + + virtual void destroy( JNIEnv * jni_env ) = 0; +protected: + void destruct( JNIEnv * jni_env ) + { jni_env->DeleteGlobalRef( m_class ); } + virtual ~JNI_type_info() {} + explicit JNI_type_info( + JNI_context const & jni, typelib_TypeDescription * td ); +}; + +struct JNI_interface_type_info : public JNI_type_info +{ + jobject m_proxy_ctor; // proxy ctor + jobject m_type; + // sorted via typelib function index + std::unique_ptr<jmethodID[]> m_methods; + + virtual void destroy( JNIEnv * jni_env ) override; + explicit JNI_interface_type_info( + JNI_context const & jni, typelib_TypeDescription * td ); + +private: + virtual ~JNI_interface_type_info() override {} +}; + +struct JNI_compound_type_info : public JNI_type_info +{ + JNI_type_info const * m_base; + // ctor( msg ) for exceptions + jmethodID m_exc_ctor; + // sorted via typelib member index + std::unique_ptr<jfieldID[]> m_fields; + + virtual void destroy( JNIEnv * jni_env ) override; + explicit JNI_compound_type_info( + JNI_context const & jni, typelib_TypeDescription * td ); + +private: + virtual ~JNI_compound_type_info() override {} +}; + +struct JNI_type_info_holder +{ + JNI_type_info * m_info; + + JNI_type_info_holder(const JNI_type_info_holder&) = delete; + const JNI_type_info_holder& operator=(const JNI_type_info_holder&) = delete; + + JNI_type_info_holder() : m_info( nullptr ) {} +}; + +typedef std::unordered_map< + OUString, JNI_type_info_holder > t_str2type; + +class JNI_info +{ + mutable ::osl::Mutex m_mutex; + mutable t_str2type m_type_map; + +public: + // These two are needed very early by find_class from within the ctor: + jclass m_class_Class; + jmethodID m_method_Class_forName; + + jobject m_object_java_env; + jobject m_object_Any_VOID; + jobject m_object_Type_UNSIGNED_SHORT; + jobject m_object_Type_UNSIGNED_LONG; + jobject m_object_Type_UNSIGNED_HYPER; + + jclass m_class_Object; + jclass m_class_Character; + jclass m_class_Boolean; + jclass m_class_Byte; + jclass m_class_Short; + jclass m_class_Integer; + jclass m_class_Long; + jclass m_class_Float; + jclass m_class_Double; + jclass m_class_String; + + jclass m_class_UnoRuntime; + jclass m_class_RuntimeException; + jclass m_class_Any; + jclass m_class_Type; + jclass m_class_TypeClass; + jclass m_class_JNI_proxy; + jclass m_class_AsynchronousFinalizer; + + jmethodID m_method_Object_toString; + jmethodID m_method_Class_getName; + jmethodID m_method_Throwable_getMessage; + jmethodID m_ctor_Character_with_char; + jmethodID m_ctor_Boolean_with_boolean; + jmethodID m_ctor_Byte_with_byte; + jmethodID m_ctor_Short_with_short; + jmethodID m_ctor_Integer_with_int; + jmethodID m_ctor_Long_with_long; + jmethodID m_ctor_Float_with_float; + jmethodID m_ctor_Double_with_double; + jmethodID m_method_Boolean_booleanValue; + jmethodID m_method_Byte_byteValue; + jmethodID m_method_Character_charValue; + jmethodID m_method_Double_doubleValue; + jmethodID m_method_Float_floatValue; + jmethodID m_method_Integer_intValue; + jmethodID m_method_Long_longValue; + jmethodID m_method_Short_shortValue; + + jmethodID m_method_IEnvironment_getRegisteredInterface; + jmethodID m_method_IEnvironment_registerInterface; + jmethodID m_method_UnoRuntime_generateOid; + jmethodID m_method_UnoRuntime_queryInterface; + jmethodID m_ctor_Any_with_Type_Object; + jfieldID m_field_Any_type; + jfieldID m_field_Any_object; + jmethodID m_ctor_Type_with_Class; + jmethodID m_ctor_Type_with_Name_TypeClass; + jfieldID m_field_Type_typeName; + jmethodID m_method_TypeClass_fromInt; + jfieldID m_field_Enum_m_value; + + jmethodID m_method_JNI_proxy_get_proxy_ctor; + jmethodID m_method_JNI_proxy_create; + jfieldID m_field_JNI_proxy_m_receiver_handle; + jfieldID m_field_JNI_proxy_m_td_handle; + jfieldID m_field_JNI_proxy_m_type; + jfieldID m_field_JNI_proxy_m_oid; + + jmethodID m_ctor_AsynchronousFinalizer; + jmethodID m_method_AsynchronousFinalizer_drain; + + ::com::sun::star::uno::TypeDescription m_XInterface_queryInterface_td; + ::com::sun::star::uno::Type const & m_Exception_type; + ::com::sun::star::uno::Type const & m_RuntimeException_type; + ::com::sun::star::uno::Type const & m_void_type; + JNI_interface_type_info const * m_XInterface_type_info; + + // noncopyable + JNI_info(const JNI_info&) = delete; + const JNI_info& operator=(const JNI_info&) = delete; + + JNI_type_info const * get_type_info( + JNI_context const & jni, + typelib_TypeDescription * type ) const; + JNI_type_info const * get_type_info( + JNI_context const & jni, + typelib_TypeDescriptionReference * type ) const; + JNI_type_info const * get_type_info( + JNI_context const & jni, + OUString const & uno_name ) const; + inline static void append_sig( + OStringBuffer * buf, typelib_TypeDescriptionReference * type, + bool use_Object_for_type_XInterface = true, bool use_slashes = true ); + + // get this + static JNI_info const * get_jni_info( + rtl::Reference< jvmaccess::UnoVirtualMachine > const & uno_vm ); + inline void destroy( JNIEnv * jni_env ); + +private: + JNI_type_info const * create_type_info( + JNI_context const & jni, typelib_TypeDescription * td ) const; + + void destruct( JNIEnv * jni_env ); + + JNI_info( JNIEnv * jni_env, jobject class_loader, + jclass classClass, jmethodID methodForName ); + ~JNI_info() {} +}; + +inline void JNI_info::destroy( JNIEnv * jni_env ) +{ + destruct( jni_env ); + delete this; +} + +inline void JNI_info::append_sig( + OStringBuffer * buf, typelib_TypeDescriptionReference * type, + bool use_Object_for_type_XInterface, bool use_slashes ) +{ + switch (type->eTypeClass) + { + case typelib_TypeClass_VOID: + buf->append( 'V' ); + break; + case typelib_TypeClass_CHAR: + buf->append( 'C' ); + break; + case typelib_TypeClass_BOOLEAN: + buf->append( 'Z' ); + break; + case typelib_TypeClass_BYTE: + buf->append( 'B' ); + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + buf->append( 'S' ); + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + buf->append( 'I' ); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + buf->append( 'J' ); + break; + case typelib_TypeClass_FLOAT: + buf->append( 'F' ); + break; + case typelib_TypeClass_DOUBLE: + buf->append( 'D' ); + break; + case typelib_TypeClass_STRING: + if ( use_slashes ) { + buf->append( "Ljava/lang/String;" ); + } else { + buf->append( "Ljava.lang.String;" ); + } + break; + case typelib_TypeClass_TYPE: + if ( use_slashes ) { + buf->append( "Lcom/sun/star/uno/Type;" ); + } else { + buf->append( "Lcom.sun.star.uno.Type;" ); + } + break; + case typelib_TypeClass_ANY: + if ( use_slashes ) { + buf->append( "Ljava/lang/Object;" ); + } else { + buf->append( "Ljava.lang.Object;" ); + } + break; + case typelib_TypeClass_ENUM: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + OUString const & uno_name = + OUString::unacquired( &type->pTypeName ); + buf->append( 'L' ); + // Erase type arguments of instantiated polymorphic struct types: + sal_Int32 i = uno_name.indexOf( '<' ); + if ( i < 0 ) { + buf->append( + OUStringToOString( + use_slashes ? uno_name.replace( '.', '/' ) : uno_name, + RTL_TEXTENCODING_JAVA_UTF8 ) ); + } else { + OUString s( uno_name.copy( 0, i ) ); + buf->append( + OUStringToOString( + use_slashes ? s.replace( '.', '/' ) : s, + RTL_TEXTENCODING_JAVA_UTF8 ) ); + } + buf->append( ';' ); + break; + } + case typelib_TypeClass_SEQUENCE: + { + buf->append( '[' ); + TypeDescr td( type ); + append_sig( + buf, reinterpret_cast<typelib_IndirectTypeDescription *>(td.get())->pType, + use_Object_for_type_XInterface, use_slashes ); + break; + } + case typelib_TypeClass_INTERFACE: + if (use_Object_for_type_XInterface && is_XInterface( type )) + { + if ( use_slashes ) { + buf->append( "Ljava/lang/Object;" ); + } else { + buf->append( "Ljava.lang.Object;" ); + } + } + else + { + OUString const & uno_name = + OUString::unacquired( &type->pTypeName ); + buf->append( 'L' ); + buf->append( + OUStringToOString( + use_slashes ? uno_name.replace( '.', '/' ) : uno_name, + RTL_TEXTENCODING_JAVA_UTF8 ) ); + buf->append( ';' ); + } + break; + default: + throw BridgeRuntimeError( + "unsupported type: " + + OUString::unacquired( &type->pTypeName ) ); + } +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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, ¶m, + 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: */ diff --git a/bridges/source/jni_uno/jni_uno2java.cxx b/bridges/source/jni_uno/jni_uno2java.cxx new file mode 100644 index 000000000..eb152c110 --- /dev/null +++ b/bridges/source/jni_uno/jni_uno2java.cxx @@ -0,0 +1,806 @@ +/* -*- 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 <atomic> +#include <cassert> +#include <cstddef> +#include <memory> + +#include <sal/alloca.h> + +#include <com/sun/star/uno/RuntimeException.hpp> + +#include <rtl/ustrbuf.hxx> + +#include "jni_bridge.h" +#include "jniunoenvironmentdata.hxx" + +namespace +{ +extern "C" +{ + + +void UNO_proxy_free( uno_ExtEnvironment * env, void * proxy ) + SAL_THROW_EXTERN_C(); + + +void UNO_proxy_acquire( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C(); + + +void UNO_proxy_release( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C(); + + +void UNO_proxy_dispatch( + uno_Interface * pUnoI, typelib_TypeDescription const * member_td, + void * uno_ret, void * uno_args[], uno_Any ** uno_exc ) + SAL_THROW_EXTERN_C(); +} +} + +namespace jni_uno +{ + + +void Bridge::handle_java_exc( + JNI_context const & jni, + JLocalAutoRef const & jo_exc, uno_Any * uno_exc ) const +{ + assert( jo_exc.is() ); + if (! jo_exc.is()) + { + throw BridgeRuntimeError( + "java exception occurred, but no java exception available!?" + + jni.get_stack_trace() ); + } + + JLocalAutoRef jo_class( jni, jni->GetObjectClass( jo_exc.get() ) ); + JLocalAutoRef jo_class_name( + jni, jni->CallObjectMethodA( + jo_class.get(), getJniInfo()->m_method_Class_getName, nullptr ) ); + jni.ensure_no_exception(); + OUString exc_name( + jstring_to_oustring( jni, static_cast<jstring>(jo_class_name.get()) ) ); + + ::com::sun::star::uno::TypeDescription td( exc_name.pData ); + if (!td.is() || (td.get()->eTypeClass != typelib_TypeClass_EXCEPTION)) + { + // call toString() + JLocalAutoRef jo_descr( + jni, jni->CallObjectMethodA( + jo_exc.get(), getJniInfo()->m_method_Object_toString, nullptr ) ); + jni.ensure_no_exception(); + throw BridgeRuntimeError( + "non-UNO exception occurred: " + + jstring_to_oustring( jni, static_cast<jstring>(jo_descr.get()) ) + + jni.get_stack_trace( jo_exc.get() ) ); + } + + std::unique_ptr< rtl_mem > uno_data( rtl_mem::allocate( td.get()->nSize ) ); + jvalue val; + val.l = jo_exc.get(); + map_to_uno( + jni, uno_data.get(), val, td.get()->pWeakRef, nullptr, + false /* no assign */, false /* no out param */ ); + +#if OSL_DEBUG_LEVEL > 0 + // patch Message, append stack trace + reinterpret_cast< ::com::sun::star::uno::Exception * >( + uno_data.get() )->Message += jni.get_stack_trace( jo_exc.get() ); +#endif + + typelib_typedescriptionreference_acquire( td.get()->pWeakRef ); + uno_exc->pType = td.get()->pWeakRef; + uno_exc->pData = uno_data.release(); + + SAL_INFO( + "bridges", + "exception occurred uno->java: [" << exc_name << "] " + << (static_cast<css::uno::Exception const *>(uno_exc->pData) + ->Message)); +} + + +void Bridge::call_java( + jobject javaI, typelib_InterfaceTypeDescription * iface_td, + sal_Int32 local_member_index, sal_Int32 function_pos_offset, + typelib_TypeDescriptionReference * return_type, + typelib_MethodParameter * params, sal_Int32 nParams, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const +{ + assert( function_pos_offset == 0 || function_pos_offset == 1 ); + + JNI_guarded_context jni( + getJniInfo(), + static_cast<JniUnoEnvironmentData *>(m_java_env->pContext)->machine); + + // assure fully initialized iface_td: + ::com::sun::star::uno::TypeDescription iface_holder; + if (! iface_td->aBase.bComplete) { + iface_holder = ::com::sun::star::uno::TypeDescription( + reinterpret_cast<typelib_TypeDescription *>(iface_td) ); + iface_holder.makeComplete(); + if (! iface_holder.get()->bComplete) { + throw BridgeRuntimeError( + "cannot make type complete: " + + OUString::unacquired(&iface_holder.get()->pTypeName) + + jni.get_stack_trace() ); + } + iface_td = reinterpret_cast<typelib_InterfaceTypeDescription *>( + iface_holder.get() ); + assert( iface_td->aBase.eTypeClass == typelib_TypeClass_INTERFACE ); + } + + // prepare java args, save param td + jvalue * java_args = static_cast<jvalue *>(alloca( sizeof (jvalue) * nParams )); + + sal_Int32 nPos; + for ( nPos = 0; nPos < nParams; ++nPos ) + { + try + { + typelib_MethodParameter const & param = params[ nPos ]; + java_args[ nPos ].l = nullptr; // if out: build up array[ 1 ] + map_to_java( + jni, &java_args[ nPos ], + uno_args[ nPos ], + param.pTypeRef, nullptr, + param.bIn /* convert uno value */, + param.bOut /* build up array[ 1 ] */ ); + } + catch (...) + { + // cleanup + for ( sal_Int32 n = 0; n < nPos; ++n ) + { + typelib_MethodParameter const & param = params[ n ]; + if (param.bOut || + typelib_TypeClass_DOUBLE < param.pTypeRef->eTypeClass) + { + jni->DeleteLocalRef( java_args[ n ].l ); + } + } + throw; + } + } + + sal_Int32 base_members = iface_td->nAllMembers - iface_td->nMembers; + assert( base_members < iface_td->nAllMembers ); + sal_Int32 base_members_function_pos = + iface_td->pMapMemberIndexToFunctionIndex[ base_members ]; + sal_Int32 member_pos = base_members + local_member_index; + SAL_WARN_IF( + member_pos >= iface_td->nAllMembers, "bridges", + "member pos out of range"); + sal_Int32 function_pos = + iface_td->pMapMemberIndexToFunctionIndex[ member_pos ] + + function_pos_offset; + SAL_WARN_IF( + (function_pos < base_members_function_pos + || function_pos >= iface_td->nMapFunctionIndexToMemberIndex), + "bridges", "illegal function index"); + function_pos -= base_members_function_pos; + + JNI_interface_type_info const * info = + static_cast< JNI_interface_type_info const * >( + getJniInfo()->get_type_info( jni, &iface_td->aBase ) ); + jmethodID method_id = info->m_methods[ function_pos ]; + +#if OSL_DEBUG_LEVEL > 0 + OUStringBuffer trace_buf( 128 ); + trace_buf.append( "calling " ); + JLocalAutoRef jo_method( + jni, jni->ToReflectedMethod( info->m_class, method_id, JNI_FALSE ) ); + jni.ensure_no_exception(); + JLocalAutoRef jo_descr( + jni, jni->CallObjectMethodA( + jo_method.get(), getJniInfo()->m_method_Object_toString, nullptr ) ); + jni.ensure_no_exception(); + trace_buf.append( jstring_to_oustring( jni, static_cast<jstring>(jo_descr.get()) ) ); + trace_buf.append( " on " ); + jo_descr.reset( + jni->CallObjectMethodA( + javaI, getJniInfo()->m_method_Object_toString, nullptr ) ); + jni.ensure_no_exception(); + trace_buf.append( jstring_to_oustring( jni, static_cast<jstring>(jo_descr.get()) ) ); + trace_buf.append( " (" ); + JLocalAutoRef jo_class( jni, jni->GetObjectClass( javaI ) ); + jo_descr.reset( + jni->CallObjectMethodA( + jo_class.get(), getJniInfo()->m_method_Object_toString, nullptr ) ); + jni.ensure_no_exception(); + trace_buf.append( jstring_to_oustring( jni, static_cast<jstring>(jo_descr.get()) ) ); + trace_buf.append( ")" ); + SAL_INFO("bridges", trace_buf.makeStringAndClear()); +#endif + + // complex return value + JLocalAutoRef java_ret( jni ); + + switch (return_type->eTypeClass) + { + case typelib_TypeClass_VOID: + jni->CallVoidMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_CHAR: + *static_cast<sal_Unicode *>(uno_ret) = + jni->CallCharMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_BOOLEAN: + *static_cast<sal_Bool *>(uno_ret) = + jni->CallBooleanMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_BYTE: + *static_cast<sal_Int8 *>(uno_ret) = + jni->CallByteMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + *static_cast<sal_Int16 *>(uno_ret) = + jni->CallShortMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + *static_cast<sal_Int32 *>(uno_ret) = + jni->CallIntMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + *static_cast<sal_Int64 *>(uno_ret) = + jni->CallLongMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_FLOAT: + *static_cast<float *>(uno_ret) = + jni->CallFloatMethodA( javaI, method_id, java_args ); + break; + case typelib_TypeClass_DOUBLE: + *static_cast<double *>(uno_ret) = + jni->CallDoubleMethodA( javaI, method_id, java_args ); + break; + default: + java_ret.reset( + jni->CallObjectMethodA( javaI, method_id, java_args ) ); + break; + } + + if (jni->ExceptionCheck()) + { + JLocalAutoRef jo_exc( jni, jni->ExceptionOccurred() ); + jni->ExceptionClear(); + + // release temp java local refs + for ( nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = params[ nPos ]; + if (param.bOut || + typelib_TypeClass_DOUBLE < param.pTypeRef->eTypeClass) + { + jni->DeleteLocalRef( java_args[ nPos ].l ); + } + } + + handle_java_exc( jni, jo_exc, *uno_exc ); + } + else // no exception + { + for ( nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = params[ nPos ]; + if (param.bOut) + { + try + { + map_to_uno( + jni, uno_args[ nPos ], + java_args[ nPos ], param.pTypeRef, nullptr, + param.bIn /* assign if inout */, + true /* out param */ ); + } + catch (...) + { + // cleanup uno pure out + for ( sal_Int32 n = 0; n < nPos; ++n ) + { + typelib_MethodParameter const & p = params[ n ]; + if (! p.bIn) + { + uno_type_destructData( + uno_args[ n ], p.pTypeRef, nullptr ); + } + } + // cleanup java temp local refs + for ( ; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & p = params[ nPos ]; + if (p.bOut || + typelib_TypeClass_DOUBLE < + p.pTypeRef->eTypeClass) + { + jni->DeleteLocalRef( java_args[ nPos ].l ); + } + } + throw; + } + jni->DeleteLocalRef( java_args[ nPos ].l ); + } + else // pure temp in param + { + if (typelib_TypeClass_DOUBLE < param.pTypeRef->eTypeClass) + jni->DeleteLocalRef( java_args[ nPos ].l ); + } + } + + // return value + if (typelib_TypeClass_DOUBLE < return_type->eTypeClass) + { + try + { + jvalue val; + val.l = java_ret.get(); + map_to_uno( + jni, uno_ret, val, return_type, nullptr, + false /* no assign */, false /* no out param */ ); + } + catch (...) + { + // cleanup uno pure out + for ( sal_Int32 i = 0; i < nParams; ++i ) + { + typelib_MethodParameter const & param = params[ i ]; + if (! param.bIn) + { + uno_type_destructData( + uno_args[ i ], param.pTypeRef, nullptr ); + } + } + throw; + } + } // else: already set integral uno return value + + // no exception occurred + *uno_exc = nullptr; + } +} + +namespace { + +// a UNO proxy wrapping a Java interface +struct UNO_proxy : public uno_Interface +{ + mutable std::atomic<std::size_t> m_ref; + Bridge const * m_bridge; + + // mapping information + jobject m_javaI; + jstring m_jo_oid; + OUString m_oid; + JNI_interface_type_info const * m_type_info; + + inline void acquire() const; + inline void release() const; + + // ctor + inline UNO_proxy( + JNI_context const & jni, Bridge const * bridge, + jobject javaI, jstring jo_oid, OUString const & oid, + JNI_interface_type_info const * info ); +}; + +} + +inline UNO_proxy::UNO_proxy( + JNI_context const & jni, Bridge const * bridge, + jobject javaI, jstring jo_oid, OUString const & oid, + JNI_interface_type_info const * info ) + : m_ref( 1 ), + m_oid( oid ), + m_type_info( info ) +{ + JNI_info const * jni_info = bridge->getJniInfo(); + JLocalAutoRef jo_string_array( + jni, jni->NewObjectArray( 1, jni_info->m_class_String, jo_oid ) ); + jni.ensure_no_exception(); + jvalue args[ 3 ]; + args[ 0 ].l = javaI; + args[ 1 ].l = jo_string_array.get(); + args[ 2 ].l = info->m_type; + jobject jo_iface = jni->CallObjectMethodA( + jni_info->m_object_java_env, + jni_info->m_method_IEnvironment_registerInterface, args ); + jni.ensure_no_exception(); + + m_javaI = jni->NewGlobalRef( jo_iface ); + m_jo_oid = static_cast<jstring>(jni->NewGlobalRef( jo_oid )); + bridge->acquire(); + m_bridge = bridge; + + // uno_Interface + uno_Interface::acquire = UNO_proxy_acquire; + uno_Interface::release = UNO_proxy_release; + uno_Interface::pDispatcher = UNO_proxy_dispatch; +} + + +inline void UNO_proxy::acquire() const +{ + if (++m_ref == 1) + { + // rebirth of proxy zombie + void * that = const_cast< UNO_proxy * >( this ); + // register at uno env + (*m_bridge->m_uno_env->registerProxyInterface)( + m_bridge->m_uno_env, &that, + UNO_proxy_free, m_oid.pData, + reinterpret_cast<typelib_InterfaceTypeDescription *>(m_type_info->m_td.get()) ); + assert( this == that ); + } +} + + +inline void UNO_proxy::release() const +{ + if (--m_ref == 0) + { + // revoke from uno env on last release + (*m_bridge->m_uno_env->revokeInterface)( + m_bridge->m_uno_env, const_cast< UNO_proxy * >( this ) ); + } +} + + +uno_Interface * Bridge::map_to_uno( + JNI_context const & jni, + jobject javaI, JNI_interface_type_info const * info ) const +{ + JLocalAutoRef jo_oid( jni, compute_oid( jni, javaI ) ); + OUString oid( jstring_to_oustring( jni, static_cast<jstring>(jo_oid.get()) ) ); + + uno_Interface * pUnoI = nullptr; + (*m_uno_env->getRegisteredInterface)( + m_uno_env, reinterpret_cast<void **>(&pUnoI), + oid.pData, reinterpret_cast<typelib_InterfaceTypeDescription *>(info->m_td.get()) ); + + if (pUnoI == nullptr) // no existing interface, register new proxy + { + // refcount initially 1 + pUnoI = new UNO_proxy( + jni, this, + javaI, static_cast<jstring>(jo_oid.get()), oid, info ); + + (*m_uno_env->registerProxyInterface)( + m_uno_env, reinterpret_cast<void **>(&pUnoI), + UNO_proxy_free, + oid.pData, reinterpret_cast<typelib_InterfaceTypeDescription *>(info->m_td.get()) ); + } + return pUnoI; +} + +} + +using namespace ::jni_uno; + +namespace +{ +extern "C" +{ + + +void UNO_proxy_free( uno_ExtEnvironment * env, void * proxy ) + SAL_THROW_EXTERN_C() +{ + UNO_proxy * that = static_cast< UNO_proxy * >( proxy ); + Bridge const * bridge = that->m_bridge; + + assert(env == bridge->m_uno_env); (void) env; + SAL_INFO("bridges", "freeing binary uno proxy: " << that->m_oid); + + try + { + JNI_guarded_context jni( + bridge->getJniInfo(), + (static_cast<JniUnoEnvironmentData *>(bridge->m_java_env->pContext) + ->machine)); + + jni->DeleteGlobalRef( that->m_javaI ); + jni->DeleteGlobalRef( that->m_jo_oid ); + } + catch (BridgeRuntimeError & err) + { + SAL_WARN( + "bridges", + "ignoring BridgeRuntimeError \"" << err.m_message << "\""); + } + catch (::jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + SAL_WARN("bridges", "attaching current thread to java failed"); + } + + bridge->release(); +#if OSL_DEBUG_LEVEL > 0 + *reinterpret_cast<int *>(that) = 0xdeadcafe; +#endif + delete that; +} + + +void UNO_proxy_acquire( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C() +{ + UNO_proxy const * that = static_cast< UNO_proxy const * >( pUnoI ); + that->acquire(); +} + + +void UNO_proxy_release( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C() +{ + UNO_proxy const * that = static_cast< UNO_proxy const * >( pUnoI ); + that->release(); +} + + +void UNO_proxy_dispatch( + uno_Interface * pUnoI, typelib_TypeDescription const * member_td, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) + SAL_THROW_EXTERN_C() +{ + UNO_proxy const * that = static_cast< UNO_proxy const * >( pUnoI ); + Bridge const * bridge = that->m_bridge; + + SAL_INFO( + "bridges", + "uno->java call: " << OUString::unacquired(&member_td->pTypeName) + << " on oid " << that->m_oid); + + try + { + switch (member_td->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + typelib_InterfaceAttributeTypeDescription const * attrib_td = + reinterpret_cast< + typelib_InterfaceAttributeTypeDescription const * >( + member_td ); + com::sun::star::uno::TypeDescription attrib_holder; + while ( attrib_td->pBaseRef != nullptr ) { + attrib_holder = com::sun::star::uno::TypeDescription( + attrib_td->pBaseRef ); + assert( + attrib_holder.get()->eTypeClass + == typelib_TypeClass_INTERFACE_ATTRIBUTE ); + attrib_td = reinterpret_cast< + typelib_InterfaceAttributeTypeDescription * >( + attrib_holder.get() ); + } + typelib_InterfaceTypeDescription * iface_td = attrib_td->pInterface; + + if (uno_ret == nullptr) // is setter method + { + typelib_MethodParameter param; + param.pTypeRef = attrib_td->pAttributeTypeRef; + param.bIn = true; + param.bOut = false; + + bridge->call_java( + that->m_javaI, iface_td, + attrib_td->nIndex, 1, // get, then set method + bridge->getJniInfo()->m_void_type.getTypeLibType(), + ¶m, 1, + nullptr, uno_args, uno_exc ); + } + else // is getter method + { + bridge->call_java( + that->m_javaI, iface_td, attrib_td->nIndex, 0, + attrib_td->pAttributeTypeRef, + nullptr, 0, // no params + uno_ret, nullptr, uno_exc ); + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + typelib_InterfaceMethodTypeDescription const * method_td = + reinterpret_cast< + typelib_InterfaceMethodTypeDescription const * >( + member_td ); + com::sun::star::uno::TypeDescription method_holder; + while ( method_td->pBaseRef != nullptr ) { + method_holder = com::sun::star::uno::TypeDescription( + method_td->pBaseRef ); + assert( + method_holder.get()->eTypeClass + == typelib_TypeClass_INTERFACE_METHOD ); + method_td = reinterpret_cast< + typelib_InterfaceMethodTypeDescription * >( + method_holder.get() ); + } + typelib_InterfaceTypeDescription * iface_td = method_td->pInterface; + + switch ( method_td->aBase.nPosition ) + { + case 0: // queryInterface() + { + TypeDescr demanded_td( + *static_cast< typelib_TypeDescriptionReference ** >( + uno_args[ 0 ] ) ); + if (demanded_td.get()->eTypeClass != + typelib_TypeClass_INTERFACE) + { + throw BridgeRuntimeError( + "queryInterface() call demands an INTERFACE type!" ); + } + + uno_Interface * pInterface = nullptr; + (*bridge->m_uno_env->getRegisteredInterface)( + bridge->m_uno_env, + reinterpret_cast<void **>(&pInterface), that->m_oid.pData, + reinterpret_cast<typelib_InterfaceTypeDescription *>(demanded_td.get()) ); + + if (pInterface == nullptr) + { + JNI_info const * jni_info = bridge->getJniInfo(); + JNI_guarded_context jni( + jni_info, + (static_cast<JniUnoEnvironmentData *>( + bridge->m_java_env->pContext) + ->machine)); + + JNI_interface_type_info const * info = + static_cast< JNI_interface_type_info const * >( + jni_info->get_type_info( jni, demanded_td.get() ) ); + + jvalue args[ 2 ]; + args[ 0 ].l = info->m_type; + args[ 1 ].l = that->m_javaI; + + JLocalAutoRef jo_ret( + jni, jni->CallStaticObjectMethodA( + jni_info->m_class_UnoRuntime, + jni_info->m_method_UnoRuntime_queryInterface, + args ) ); + + if (jni->ExceptionCheck()) + { + JLocalAutoRef jo_exc( jni, jni->ExceptionOccurred() ); + jni->ExceptionClear(); + bridge->handle_java_exc( jni, jo_exc, *uno_exc ); + } + else + { + if (jo_ret.is()) + { + SAL_WARN_IF( + (jstring_to_oustring( + jni, + static_cast<jstring>( + JLocalAutoRef( + jni, compute_oid(jni, jo_ret.get())) + .get())) + != that->m_oid), + "bridges", "different oids"); + // refcount initially 1 + uno_Interface * pUnoI2 = new UNO_proxy( + jni, bridge, jo_ret.get(), + that->m_jo_oid, that->m_oid, info ); + + (*bridge->m_uno_env->registerProxyInterface)( + bridge->m_uno_env, + reinterpret_cast<void **>(&pUnoI2), + UNO_proxy_free, that->m_oid.pData, + reinterpret_cast< + typelib_InterfaceTypeDescription * >( + info->m_td.get() ) ); + + uno_any_construct( + static_cast<uno_Any *>(uno_ret), &pUnoI2, + demanded_td.get(), nullptr ); + (*pUnoI2->release)( pUnoI2 ); + } + else // object does not support demanded interface + { + uno_any_construct( + static_cast< uno_Any * >( uno_ret ), + nullptr, nullptr, nullptr ); + } + // no exception occurred + *uno_exc = nullptr; + } + } + else + { + uno_any_construct( + static_cast< uno_Any * >( uno_ret ), + &pInterface, demanded_td.get(), nullptr ); + (*pInterface->release)( pInterface ); + *uno_exc = nullptr; + } + break; + } + case 1: // acquire this proxy + that->acquire(); + *uno_exc = nullptr; + break; + case 2: // release this proxy + that->release(); + *uno_exc = nullptr; + break; + default: // arbitrary method call + bridge->call_java( + that->m_javaI, iface_td, method_td->nIndex, 0, + method_td->pReturnTypeRef, + method_td->pParams, method_td->nParams, + uno_ret, uno_args, uno_exc ); + break; + } + break; + } + default: + { + throw BridgeRuntimeError( + "illegal member type description!" ); + } + } + } + catch (BridgeRuntimeError & err) + { + OUStringBuffer buf( 128 ); + buf.append( "[jni_uno bridge error] UNO calling Java method " ); + if (member_td->eTypeClass == typelib_TypeClass_INTERFACE_METHOD || + member_td->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE) + { + buf.append( OUString::unacquired( + &reinterpret_cast< + typelib_InterfaceMemberTypeDescription const * >( + member_td )->pMemberName ) ); + } + buf.append( ": " ); + buf.append( err.m_message ); + // binary identical struct + ::com::sun::star::uno::RuntimeException exc( + buf.makeStringAndClear(), + ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XInterface >() ); + ::com::sun::star::uno::Type const & exc_type = cppu::UnoType<decltype(exc)>::get(); + uno_type_any_construct( *uno_exc, &exc, exc_type.getTypeLibType(), nullptr ); + SAL_INFO("bridges", exc.Message); + } + catch (::jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + // binary identical struct + ::com::sun::star::uno::RuntimeException exc( + "[jni_uno bridge error] attaching current thread to java failed!", + ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XInterface >() ); + ::com::sun::star::uno::Type const & exc_type = cppu::UnoType<decltype(exc)>::get(); + uno_type_any_construct( *uno_exc, &exc, exc_type.getTypeLibType(), nullptr ); + SAL_WARN("bridges", exc.Message); + } +} + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/jni_uno/jniunoenvironmentdata.hxx b/bridges/source/jni_uno/jniunoenvironmentdata.hxx new file mode 100644 index 000000000..091e6dbd9 --- /dev/null +++ b/bridges/source/jni_uno/jniunoenvironmentdata.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNIUNOENVIRONMENTDATA_HXX +#define INCLUDED_BRIDGES_SOURCE_JNI_UNO_JNIUNOENVIRONMENTDATA_HXX + +#include <sal/config.h> + +#include <jni.h> + +#include <jvmaccess/unovirtualmachine.hxx> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> + +#include "jni_info.h" + +namespace jni_uno { + +// The pContext payload of a JNI uno_Environment: +struct JniUnoEnvironmentData { + JniUnoEnvironmentData(const JniUnoEnvironmentData&) = delete; + const JniUnoEnvironmentData& operator=(const JniUnoEnvironmentData&) = delete; + + explicit JniUnoEnvironmentData( + rtl::Reference<jvmaccess::UnoVirtualMachine> const & theMachine): + machine(theMachine), info(JNI_info::get_jni_info(theMachine)), + asynchronousFinalizer(nullptr) + {} + + rtl::Reference<jvmaccess::UnoVirtualMachine> const machine; + JNI_info const * const info; + + osl::Mutex mutex; + jobject asynchronousFinalizer; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/bridges/source/jni_uno/nativethreadpool.cxx b/bridges/source/jni_uno/nativethreadpool.cxx new file mode 100644 index 000000000..4fe4a0a2f --- /dev/null +++ b/bridges/source/jni_uno/nativethreadpool.cxx @@ -0,0 +1,226 @@ +/* -*- 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 <string.h> +#include <jvmaccess/virtualmachine.hxx> +#include <rtl/byteseq.h> +#include <rtl/byteseq.hxx> +#include <rtl/ref.hxx> +#include <sal/types.h> +#include <uno/threadpool.h> + +#include <jni.h> + +#include <new> + +/* The native implementation part of + * jurt/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java. + */ + +namespace { + +struct Pool { + Pool(rtl::Reference< jvmaccess::VirtualMachine > const & theVirtualMachine, + jmethodID theExecute, uno_ThreadPool thePool): + virtualMachine(theVirtualMachine), execute(theExecute), pool(thePool) {} + + rtl::Reference< jvmaccess::VirtualMachine > virtualMachine; + jmethodID execute; + uno_ThreadPool pool; +}; + +struct Job { + Job(Pool * thePool, jobject theJob): pool(thePool), job(theJob) {} + + Pool * pool; + jobject job; +}; + +void throwOutOfMemory(JNIEnv * env) { + jclass c = env->FindClass("java/lang/OutOfMemoryError"); + if (c != nullptr) { + env->ThrowNew(c, ""); + } +} + +} + +extern "C" { + +static void executeRequest(void * data) { + Job * job = static_cast< Job * >(data); + try { + jvmaccess::VirtualMachine::AttachGuard guard(job->pool->virtualMachine); + JNIEnv * env = guard.getEnvironment(); + // Failure of the following Job.execute Java call is ignored; if that + // call fails, it should be due to a java.lang.Error, which is not + // handled well, anyway: + env->CallObjectMethod(job->job, job->pool->execute); + env->DeleteGlobalRef(job->job); + delete job; + } catch (const jvmaccess::VirtualMachine::AttachGuard::CreationException &) { + //TODO: DeleteGlobalRef(job->job) + delete job; + } +} + +} + +extern "C" SAL_JNI_EXPORT jbyteArray JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_threadId( + JNIEnv * env, SAL_UNUSED_PARAMETER jclass) SAL_THROW_EXTERN_C() +{ + sal_Sequence * s = nullptr; + uno_getIdOfCurrentThread(&s); //TODO: out of memory + uno_releaseIdFromCurrentThread(); + rtl::ByteSequence seq(s); + rtl_byte_sequence_release(s); + sal_Int32 n = seq.getLength(); + jbyteArray a = env->NewByteArray(n); + // sal_Int32 and jsize are compatible here + if (a == nullptr) { + return nullptr; + } + void * p = env->GetPrimitiveArrayCritical(a, nullptr); + if (p == nullptr) { + return nullptr; + } + memcpy(p, seq.getConstArray(), n); + // sal_Int8 and jbyte ought to be compatible + env->ReleasePrimitiveArrayCritical(a, p, 0); + return a; +} + +extern "C" SAL_JNI_EXPORT jlong JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_create( + JNIEnv * env, SAL_UNUSED_PARAMETER jclass) SAL_THROW_EXTERN_C() +{ + JavaVM * vm; + if (env->GetJavaVM(&vm) != JNI_OK) { //TODO: no Java exception raised? + jclass c = env->FindClass("java/lang/RuntimeException"); + if (c != nullptr) { + env->ThrowNew(c, "JNI GetJavaVM failed"); + } + return 0; + } + jclass c = env->FindClass("com/sun/star/lib/uno/environments/remote/Job"); + if (c == nullptr) { + return 0; + } + jmethodID execute = env->GetMethodID(c, "execute", "()Ljava/lang/Object;"); + if (execute == nullptr) { + return 0; + } + try { + return reinterpret_cast< jlong >(new Pool( + new jvmaccess::VirtualMachine(vm, env->GetVersion(), false, env), + execute, uno_threadpool_create())); + } catch (const std::bad_alloc &) { + throwOutOfMemory(env); + return 0; + } +} + +extern "C" SAL_JNI_EXPORT void JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_attach( + SAL_UNUSED_PARAMETER JNIEnv *, SAL_UNUSED_PARAMETER jclass, jlong pool) + SAL_THROW_EXTERN_C() +{ + uno_threadpool_attach(reinterpret_cast< Pool * >(pool)->pool); +} + +extern "C" SAL_JNI_EXPORT jobject JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_enter( + JNIEnv * env, SAL_UNUSED_PARAMETER jclass, jlong pool) SAL_THROW_EXTERN_C() +{ + jobject job; + uno_threadpool_enter( + reinterpret_cast< Pool * >(pool)->pool, + reinterpret_cast< void ** >(&job)); + if (job == nullptr) { + return nullptr; + } + jobject ref = env->NewLocalRef(job); + env->DeleteGlobalRef(job); + return ref; +} + +extern "C" SAL_JNI_EXPORT void JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_detach( + SAL_UNUSED_PARAMETER JNIEnv *, SAL_UNUSED_PARAMETER jclass, jlong pool) + SAL_THROW_EXTERN_C() +{ + uno_threadpool_detach(reinterpret_cast< Pool * >(pool)->pool); +} + +extern "C" SAL_JNI_EXPORT void JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_putJob( + JNIEnv * env, SAL_UNUSED_PARAMETER jclass, jlong pool, jbyteArray threadId, + jobject job, jboolean request, jboolean oneWay) SAL_THROW_EXTERN_C() +{ + void * s = env->GetPrimitiveArrayCritical(threadId, nullptr); + if (s == nullptr) { + return; + } + rtl::ByteSequence seq( + static_cast< sal_Int8 * >(s), env->GetArrayLength(threadId)); + // sal_Int8 and jbyte ought to be compatible; sal_Int32 and jsize are + // compatible here + //TODO: out of memory + env->ReleasePrimitiveArrayCritical(threadId, s, JNI_ABORT); + Pool * p = reinterpret_cast< Pool * >(pool); + jobject ref = env->NewGlobalRef(job); + if (ref == nullptr) { + return; + } + Job * j = nullptr; + if (request) { + j = new(std::nothrow) Job(p, ref); + if (j == nullptr) { + env->DeleteGlobalRef(ref); + throwOutOfMemory(env); + return; + } + } + uno_threadpool_putJob( + p->pool, seq.getHandle(), + request ? static_cast< void * >(j) : static_cast< void * >(ref), + request ? executeRequest : nullptr, oneWay); +} + +extern "C" SAL_JNI_EXPORT void JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_dispose( + SAL_UNUSED_PARAMETER JNIEnv *, SAL_UNUSED_PARAMETER jclass, jlong pool) + SAL_THROW_EXTERN_C() +{ + uno_threadpool_dispose(reinterpret_cast< Pool * >(pool)->pool); +} + +extern "C" SAL_JNI_EXPORT void JNICALL +Java_com_sun_star_lib_uno_environments_remote_NativeThreadPool_destroy( + SAL_UNUSED_PARAMETER JNIEnv *, SAL_UNUSED_PARAMETER jclass, jlong pool) + SAL_THROW_EXTERN_C() +{ + Pool * p = reinterpret_cast< Pool * >(pool); + uno_threadpool_destroy(p->pool); + delete p; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |