1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
26 #include "jni_bridge.h"
27 #include "jni_helper.h"
28 #include "jniunoenvironmentdata.hxx"
30 #include <jvmaccess/unovirtualmachine.hxx>
31 #include <rtl/ref.hxx>
32 #include <uno/lbnames.h>
34 using namespace ::jni_uno
;
42 void Mapping_acquire( uno_Mapping
* mapping
) noexcept
44 Mapping
const * that
= static_cast< Mapping
const * >( mapping
);
45 that
->m_bridge
->acquire();
49 void Mapping_release( uno_Mapping
* mapping
) noexcept
51 Mapping
const * that
= static_cast< Mapping
const * >( mapping
);
52 that
->m_bridge
->release();
56 void Mapping_map_to_uno(
57 uno_Mapping
* mapping
, void ** ppOut
,
58 void * pIn
, typelib_InterfaceTypeDescription
* td
) noexcept
60 uno_Interface
** ppUnoI
= reinterpret_cast<uno_Interface
**>(ppOut
);
61 jobject javaI
= static_cast<jobject
>(pIn
);
63 static_assert(sizeof (void *) == sizeof (jobject
), "must be the same size");
64 assert(ppUnoI
!= nullptr);
65 assert(td
!= nullptr);
69 if (*ppUnoI
!= nullptr)
71 uno_Interface
* p
= *ppUnoI
;
80 Bridge
const * bridge
=
81 static_cast< Mapping
const * >( mapping
)->m_bridge
;
82 JNI_guarded_context
jni(
84 (static_cast<jni_uno::JniUnoEnvironmentData
*>(
85 bridge
->m_java_env
->pContext
)
88 JNI_interface_type_info
const * info
=
89 static_cast< JNI_interface_type_info
const * >(
90 bridge
->getJniInfo()->get_type_info(
92 uno_Interface
* pUnoI
= bridge
->map_to_uno( jni
, javaI
, info
);
93 if (*ppUnoI
!= nullptr)
95 uno_Interface
* p
= *ppUnoI
;
100 catch (const BridgeRuntimeError
& err
)
104 "ignoring BridgeRuntimeError \"" << err
.m_message
<< "\"");
106 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException
&)
108 SAL_WARN("bridges", "attaching current thread to java failed");
114 void Mapping_map_to_java(
115 uno_Mapping
* mapping
, void ** ppOut
,
116 void * pIn
, typelib_InterfaceTypeDescription
* td
) noexcept
118 jobject
* ppJavaI
= reinterpret_cast<jobject
*>(ppOut
);
119 uno_Interface
* pUnoI
= static_cast<uno_Interface
*>(pIn
);
121 static_assert(sizeof (void *) == sizeof (jobject
), "must be the same size");
122 assert(ppJavaI
!= nullptr);
123 assert(td
!= nullptr);
127 if (pUnoI
== nullptr)
129 if (*ppJavaI
!= nullptr)
131 Bridge
const * bridge
=
132 static_cast< Mapping
const * >( mapping
)->m_bridge
;
133 JNI_guarded_context
jni(
134 bridge
->getJniInfo(),
135 (static_cast<jni_uno::JniUnoEnvironmentData
*>(
136 bridge
->m_java_env
->pContext
)
138 jni
->DeleteGlobalRef( *ppJavaI
);
144 Bridge
const * bridge
=
145 static_cast< Mapping
const * >( mapping
)->m_bridge
;
146 JNI_guarded_context
jni(
147 bridge
->getJniInfo(),
148 (static_cast<jni_uno::JniUnoEnvironmentData
*>(
149 bridge
->m_java_env
->pContext
)
152 JNI_interface_type_info
const * info
=
153 static_cast< JNI_interface_type_info
const * >(
154 bridge
->getJniInfo()->get_type_info(
156 jobject jlocal
= bridge
->map_to_java( jni
, pUnoI
, info
);
157 if (*ppJavaI
!= nullptr)
158 jni
->DeleteGlobalRef( *ppJavaI
);
159 *ppJavaI
= jni
->NewGlobalRef( jlocal
);
160 jni
->DeleteLocalRef( jlocal
);
163 catch (const BridgeRuntimeError
& err
)
167 "ignoring BridgeRuntimeError \"" << err
.m_message
<< "\"");
169 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException
&)
171 SAL_WARN("bridges", "attaching current thread to java failed");
176 void Bridge_free( uno_Mapping
* mapping
) noexcept
178 Mapping
* that
= static_cast< Mapping
* >( mapping
);
179 delete that
->m_bridge
;
190 void Bridge::acquire() const
195 if (m_registered_java2uno
)
197 uno_Mapping
* mapping
= const_cast< Mapping
* >( &m_java2uno
);
199 &mapping
, Bridge_free
,
200 m_java_env
, &m_uno_env
->aBase
, nullptr );
204 uno_Mapping
* mapping
= const_cast< Mapping
* >( &m_uno2java
);
206 &mapping
, Bridge_free
,
207 &m_uno_env
->aBase
, m_java_env
, nullptr );
212 void Bridge::release() const
217 m_registered_java2uno
218 ? const_cast< Mapping
* >( &m_java2uno
)
219 : const_cast< Mapping
* >( &m_uno2java
) );
225 uno_Environment
* java_env
, uno_ExtEnvironment
* uno_env
,
226 bool registered_java2uno
)
228 m_uno_env( uno_env
),
229 m_java_env( java_env
),
230 m_registered_java2uno( registered_java2uno
)
232 assert(m_java_env
!= nullptr);
233 assert(m_uno_env
!= nullptr);
235 // uno_initEnvironment (below) cannot report errors directly, so it clears
236 // its pContext upon error to indirectly report errors from here:
237 if (static_cast<jni_uno::JniUnoEnvironmentData
*>(m_java_env
->pContext
)
240 throw BridgeRuntimeError(u
"error during JNI-UNO's uno_initEnvironment"_ustr
);
243 (*m_uno_env
->aBase
.acquire
)( &m_uno_env
->aBase
);
244 (*m_java_env
->acquire
)( m_java_env
);
247 m_java2uno
.acquire
= Mapping_acquire
;
248 m_java2uno
.release
= Mapping_release
;
249 m_java2uno
.mapInterface
= Mapping_map_to_uno
;
250 m_java2uno
.m_bridge
= this;
252 m_uno2java
.acquire
= Mapping_acquire
;
253 m_uno2java
.release
= Mapping_release
;
254 m_uno2java
.mapInterface
= Mapping_map_to_java
;
255 m_uno2java
.m_bridge
= this;
261 (*m_java_env
->release
)( m_java_env
);
262 (*m_uno_env
->aBase
.release
)( &m_uno_env
->aBase
);
265 JNI_info
const * Bridge::getJniInfo() const {
266 return static_cast<jni_uno::JniUnoEnvironmentData
*>(m_java_env
->pContext
)
270 void JNI_context::java_exc_occurred() const
272 // !don't rely on JNI_info!
274 JLocalAutoRef
jo_exc( *this, m_env
->ExceptionOccurred() );
275 m_env
->ExceptionClear();
279 throw BridgeRuntimeError(
280 "java exception occurred, but not available!?" +
284 // call toString(); don't rely on m_jni_info
285 jclass jo_class
= m_env
->FindClass( "java/lang/Object" );
286 if (m_env
->ExceptionCheck())
288 m_env
->ExceptionClear();
289 throw BridgeRuntimeError(
290 "cannot get class java.lang.Object!" + get_stack_trace() );
292 JLocalAutoRef
jo_Object( *this, jo_class
);
293 // method Object.toString()
294 jmethodID method_Object_toString
= m_env
->GetMethodID(
295 static_cast<jclass
>(jo_Object
.get()), "toString", "()Ljava/lang/String;" );
296 if (m_env
->ExceptionCheck())
298 m_env
->ExceptionClear();
299 throw BridgeRuntimeError(
300 "cannot get method id of java.lang.Object.toString()!" +
303 assert(method_Object_toString
!= nullptr);
305 JLocalAutoRef
jo_descr(
306 *this, m_env
->CallObjectMethodA(
307 jo_exc
.get(), method_Object_toString
, nullptr ) );
308 if (m_env
->ExceptionCheck()) // no chance at all
310 m_env
->ExceptionClear();
311 throw BridgeRuntimeError(
312 "error examining java exception object!" +
316 jsize len
= m_env
->GetStringLength( static_cast<jstring
>(jo_descr
.get()) );
317 std::unique_ptr
< rtl_mem
> ustr_mem(
319 sizeof (rtl_uString
) + (len
* sizeof (sal_Unicode
)) ) );
320 rtl_uString
* ustr
= reinterpret_cast<rtl_uString
*>(ustr_mem
.get());
321 m_env
->GetStringRegion( static_cast<jstring
>(jo_descr
.get()), 0, len
, reinterpret_cast<jchar
*>(ustr
->buffer
) );
322 if (m_env
->ExceptionCheck())
324 m_env
->ExceptionClear();
325 throw BridgeRuntimeError(
326 "invalid java string object!" + get_stack_trace() );
330 ustr
->buffer
[ len
] = '\0';
331 OUString
message( reinterpret_cast<rtl_uString
*>(ustr_mem
.release()), SAL_NO_ACQUIRE
);
333 throw BridgeRuntimeError( message
+ get_stack_trace( jo_exc
.get() ) );
337 void JNI_context::getClassForName(
338 jclass
* classClass
, jmethodID
* methodForName
) const
340 jclass c
= m_env
->FindClass("java/lang/Class");
342 *methodForName
= m_env
->GetStaticMethodID(
344 "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
350 jclass
JNI_context::findClass(
351 char const * name
, jclass classClass
, jmethodID methodForName
,
352 bool inException
) const
355 JLocalAutoRef
s(*this, m_env
->NewStringUTF(name
));
360 a
[2].l
= m_class_loader
;
361 c
= static_cast< jclass
>(
362 m_env
->CallStaticObjectMethodA(classClass
, methodForName
, a
));
365 ensure_no_exception();
371 OUString
JNI_context::get_stack_trace( jobject jo_exc
) const
373 JLocalAutoRef
jo_JNI_proxy(
375 find_class( *this, "com.sun.star.bridges.jni_uno.JNI_proxy", true ) );
376 if (assert_no_exception())
378 // static method JNI_proxy.get_stack_trace()
379 jmethodID method
= m_env
->GetStaticMethodID(
380 static_cast<jclass
>(jo_JNI_proxy
.get()), "get_stack_trace",
381 "(Ljava/lang/Throwable;)Ljava/lang/String;" );
382 if (assert_no_exception() && (method
!= nullptr))
386 JLocalAutoRef
jo_stack_trace(
387 *this, m_env
->CallStaticObjectMethodA(
388 static_cast<jclass
>(jo_JNI_proxy
.get()), method
, &arg
) );
389 if (assert_no_exception())
392 m_env
->GetStringLength( static_cast<jstring
>(jo_stack_trace
.get()) );
393 std::unique_ptr
< rtl_mem
> ustr_mem(
395 sizeof (rtl_uString
) + (len
* sizeof (sal_Unicode
)) ) );
396 rtl_uString
* ustr
= reinterpret_cast<rtl_uString
*>(ustr_mem
.get());
397 m_env
->GetStringRegion(
398 static_cast<jstring
>(jo_stack_trace
.get()), 0, len
, reinterpret_cast<jchar
*>(ustr
->buffer
) );
399 if (assert_no_exception())
403 ustr
->buffer
[ len
] = '\0';
405 reinterpret_cast<rtl_uString
*>(ustr_mem
.release()), SAL_NO_ACQUIRE
);
415 using namespace ::jni_uno
;
419 static void java_env_dispose(uno_Environment
* env
) {
421 = static_cast<jni_uno::JniUnoEnvironmentData
*>(env
->pContext
);
422 if (envData
== nullptr) return;
426 std::unique_lock
g(envData
->mutex
);
427 async
= envData
->asynchronousFinalizer
;
428 envData
->asynchronousFinalizer
= nullptr;
430 if (async
== nullptr) return;
433 JNI_guarded_context
jni(envData
->info
, envData
->machine
);
434 jni
->CallObjectMethodA(
435 async
, envData
->info
->m_method_AsynchronousFinalizer_drain
,
437 jni
.ensure_no_exception();
438 jni
->DeleteGlobalRef(async
);
439 } catch (const BridgeRuntimeError
& e
) {
442 "ignoring BridgeRuntimeError \"" << e
.m_message
<< "\"");
444 jvmaccess::VirtualMachine::AttachGuard::CreationException
&)
448 ("ignoring jvmaccess::VirtualMachine::AttachGuard"
449 "::CreationException"));
453 static void java_env_disposing(uno_Environment
* env
) {
454 java_env_dispose(env
);
455 delete static_cast<jni_uno::JniUnoEnvironmentData
*>(env
->pContext
);
458 #ifdef DISABLE_DYNLOADING
459 #define uno_initEnvironment java_uno_initEnvironment
463 SAL_DLLPUBLIC_EXPORT
void uno_initEnvironment( uno_Environment
* java_env
) noexcept
466 // JavaComponentLoader::getJavaLoader (in
467 // stoc/source/javaloader/javaloader.cxx) stores a
468 // jvmaccess::UnoVirtualMachine pointer into java_env->pContext; replace
469 // it here with either a pointer to a full JniUnoEnvironmentData upon
470 // success, or with a null pointer upon failure (as this function cannot
471 // directly report back failure, so it uses that way to indirectly
472 // report failure later from within the Bridge ctor):
473 rtl::Reference
<jvmaccess::UnoVirtualMachine
> vm(
474 static_cast<jvmaccess::UnoVirtualMachine
*>(java_env
->pContext
));
475 java_env
->pContext
= nullptr;
476 java_env
->dispose
= java_env_dispose
;
477 java_env
->environmentDisposing
= java_env_disposing
;
478 java_env
->pExtEnv
= nullptr; // no extended support
479 std::unique_ptr
<jni_uno::JniUnoEnvironmentData
> envData(
480 new jni_uno::JniUnoEnvironmentData(vm
));
482 JNI_guarded_context
jni(envData
->info
, envData
->machine
);
486 envData
->info
->m_class_AsynchronousFinalizer
,
487 envData
->info
->m_ctor_AsynchronousFinalizer
));
488 jni
.ensure_no_exception();
489 envData
->asynchronousFinalizer
= jni
->NewGlobalRef(ref
.get());
490 jni
.ensure_no_exception();
492 java_env
->pContext
= envData
.release();
493 } catch (const BridgeRuntimeError
& e
) {
494 SAL_WARN("bridges", "BridgeRuntimeError \"" << e
.m_message
<< "\"");
495 } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException
&) {
498 "jvmaccess::VirtualMachine::AttachGuard::CreationException");
502 #ifdef DISABLE_DYNLOADING
503 #define uno_ext_getMapping java_uno_ext_getMapping
507 SAL_DLLPUBLIC_EXPORT
void uno_ext_getMapping(
508 uno_Mapping
** ppMapping
, uno_Environment
* pFrom
, uno_Environment
* pTo
) noexcept
510 assert(ppMapping
!= nullptr);
511 assert(pFrom
!= nullptr);
512 assert(pTo
!= nullptr);
513 if (*ppMapping
!= nullptr)
515 (*(*ppMapping
)->release
)( *ppMapping
);
516 *ppMapping
= nullptr;
519 static_assert(int(JNI_FALSE
) == int(false), "must be equal");
520 static_assert(int(JNI_TRUE
) == int(true), "must be equal");
521 static_assert(sizeof (jboolean
) == sizeof (sal_Bool
), "must be the same size");
522 static_assert(sizeof (jchar
) == sizeof (sal_Unicode
), "must be the same size");
523 static_assert(sizeof (jdouble
) == sizeof (double), "must be the same size");
524 static_assert(sizeof (jfloat
) == sizeof (float), "must be the same size");
525 static_assert(sizeof (jbyte
) == sizeof (sal_Int8
), "must be the same size");
526 static_assert(sizeof (jshort
) == sizeof (sal_Int16
), "must be the same size");
527 static_assert(sizeof (jint
) == sizeof (sal_Int32
), "must be the same size");
528 static_assert(sizeof (jlong
) == sizeof (sal_Int64
), "must be the same size");
530 OUString
const & from_env_typename
=
531 OUString::unacquired( &pFrom
->pTypeName
);
532 OUString
const & to_env_typename
=
533 OUString::unacquired( &pTo
->pTypeName
);
535 uno_Mapping
* mapping
= nullptr;
539 if ( from_env_typename
== UNO_LB_JAVA
&& to_env_typename
== UNO_LB_UNO
)
542 new Bridge( pFrom
, pTo
->pExtEnv
, true ); // ref count = 1
543 mapping
= &bridge
->m_java2uno
;
545 &mapping
, Bridge_free
,
546 pFrom
, &pTo
->pExtEnv
->aBase
, nullptr );
547 // coverity[leaked_storage] - on purpose
549 else if ( from_env_typename
== UNO_LB_UNO
&& to_env_typename
== UNO_LB_JAVA
)
552 new Bridge( pTo
, pFrom
->pExtEnv
, false ); // ref count = 1
553 mapping
= &bridge
->m_uno2java
;
555 &mapping
, Bridge_free
,
556 &pFrom
->pExtEnv
->aBase
, pTo
, nullptr );
557 // coverity[leaked_storage] - on purpose
560 catch (const BridgeRuntimeError
& err
)
562 SAL_WARN("bridges", "BridgeRuntimeError \"" << err
.m_message
<< "\"");
565 *ppMapping
= mapping
;
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */