bump product version to 5.0.4.1
[LibreOffice.git] / bridges / source / jni_uno / jni_bridge.cxx
blob75483733f6ccb33472c1fc374cff877510f592e4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cassert>
23 #include <memory>
25 #include "jni_bridge.h"
26 #include "jniunoenvironmentdata.hxx"
28 #include "jvmaccess/unovirtualmachine.hxx"
29 #include "rtl/ref.hxx"
30 #include "rtl/strbuf.hxx"
31 #include "uno/lbnames.h"
33 using namespace ::osl;
34 using namespace ::jni_uno;
36 namespace
38 extern "C"
42 void SAL_CALL Mapping_acquire( uno_Mapping * mapping )
43 SAL_THROW_EXTERN_C()
45 Mapping const * that = static_cast< Mapping const * >( mapping );
46 that->m_bridge->acquire();
50 void SAL_CALL Mapping_release( uno_Mapping * mapping )
51 SAL_THROW_EXTERN_C()
53 Mapping const * that = static_cast< Mapping const * >( mapping );
54 that->m_bridge->release();
58 void SAL_CALL Mapping_map_to_uno(
59 uno_Mapping * mapping, void ** ppOut,
60 void * pIn, typelib_InterfaceTypeDescription * td )
61 SAL_THROW_EXTERN_C()
63 uno_Interface ** ppUnoI = reinterpret_cast<uno_Interface **>(ppOut);
64 jobject javaI = static_cast<jobject>(pIn);
66 static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
67 assert(ppUnoI != 0);
68 assert(td != 0);
70 if (0 == javaI)
72 if (0 != *ppUnoI)
74 uno_Interface * p = *ppUnoI;
75 (*p->release)( p );
76 *ppUnoI = 0;
79 else
81 try
83 Bridge const * bridge =
84 static_cast< Mapping const * >( mapping )->m_bridge;
85 JNI_guarded_context jni(
86 bridge->getJniInfo(),
87 (static_cast<jni_uno::JniUnoEnvironmentData *>(
88 bridge->m_java_env->pContext)
89 ->machine));
91 JNI_interface_type_info const * info =
92 static_cast< JNI_interface_type_info const * >(
93 bridge->getJniInfo()->get_type_info(
94 jni, &td->aBase ) );
95 uno_Interface * pUnoI = bridge->map_to_uno( jni, javaI, info );
96 if (0 != *ppUnoI)
98 uno_Interface * p = *ppUnoI;
99 (*p->release)( p );
101 *ppUnoI = pUnoI;
103 catch (const BridgeRuntimeError & err)
105 SAL_WARN(
106 "bridges",
107 "ingoring BridgeRuntimeError \"" << err.m_message << "\"");
109 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
111 SAL_WARN("bridges", "attaching current thread to java failed");
117 void SAL_CALL Mapping_map_to_java(
118 uno_Mapping * mapping, void ** ppOut,
119 void * pIn, typelib_InterfaceTypeDescription * td )
120 SAL_THROW_EXTERN_C()
122 jobject * ppJavaI = reinterpret_cast<jobject *>(ppOut);
123 uno_Interface * pUnoI = static_cast<uno_Interface *>(pIn);
125 static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
126 assert(ppJavaI != 0);
127 assert(td != 0);
131 if (0 == pUnoI)
133 if (0 != *ppJavaI)
135 Bridge const * bridge =
136 static_cast< Mapping const * >( mapping )->m_bridge;
137 JNI_guarded_context jni(
138 bridge->getJniInfo(),
139 (static_cast<jni_uno::JniUnoEnvironmentData *>(
140 bridge->m_java_env->pContext)
141 ->machine));
142 jni->DeleteGlobalRef( *ppJavaI );
143 *ppJavaI = 0;
146 else
148 Bridge const * bridge =
149 static_cast< Mapping const * >( mapping )->m_bridge;
150 JNI_guarded_context jni(
151 bridge->getJniInfo(),
152 (static_cast<jni_uno::JniUnoEnvironmentData *>(
153 bridge->m_java_env->pContext)
154 ->machine));
156 JNI_interface_type_info const * info =
157 static_cast< JNI_interface_type_info const * >(
158 bridge->getJniInfo()->get_type_info(
159 jni, &td->aBase ) );
160 jobject jlocal = bridge->map_to_java( jni, pUnoI, info );
161 if (0 != *ppJavaI)
162 jni->DeleteGlobalRef( *ppJavaI );
163 *ppJavaI = jni->NewGlobalRef( jlocal );
164 jni->DeleteLocalRef( jlocal );
167 catch (const BridgeRuntimeError & err)
169 SAL_WARN(
170 "bridges",
171 "ingoring BridgeRuntimeError \"" << err.m_message << "\"");
173 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
175 SAL_WARN("bridges", "attaching current thread to java failed");
180 void SAL_CALL Bridge_free( uno_Mapping * mapping )
181 SAL_THROW_EXTERN_C()
183 Mapping * that = static_cast< Mapping * >( mapping );
184 delete that->m_bridge;
191 namespace jni_uno
195 void Bridge::acquire() const
197 if (1 == osl_atomic_increment( &m_ref ))
199 if (m_registered_java2uno)
201 uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
202 uno_registerMapping(
203 &mapping, Bridge_free,
204 m_java_env, &m_uno_env->aBase, 0 );
206 else
208 uno_Mapping * mapping = const_cast< Mapping * >( &m_uno2java );
209 uno_registerMapping(
210 &mapping, Bridge_free,
211 &m_uno_env->aBase, m_java_env, 0 );
217 void Bridge::release() const
219 if (! osl_atomic_decrement( &m_ref ))
221 uno_revokeMapping(
222 m_registered_java2uno
223 ? const_cast< Mapping * >( &m_java2uno )
224 : const_cast< Mapping * >( &m_uno2java ) );
229 Bridge::Bridge(
230 uno_Environment * java_env, uno_ExtEnvironment * uno_env,
231 bool registered_java2uno )
232 : m_ref( 1 ),
233 m_uno_env( uno_env ),
234 m_java_env( java_env ),
235 m_registered_java2uno( registered_java2uno )
237 assert(m_java_env != 0);
238 assert(m_uno_env != 0);
240 // uno_initEnvironment (below) cannot report errors directly, so it clears
241 // its pContext upon error to indirectly report errors from here:
242 if (static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
243 == nullptr)
245 throw BridgeRuntimeError("error during JNI-UNO's uno_initEnvironment");
248 (*m_uno_env->aBase.acquire)( &m_uno_env->aBase );
249 (*m_java_env->acquire)( m_java_env );
251 // java2uno
252 m_java2uno.acquire = Mapping_acquire;
253 m_java2uno.release = Mapping_release;
254 m_java2uno.mapInterface = Mapping_map_to_uno;
255 m_java2uno.m_bridge = this;
256 // uno2java
257 m_uno2java.acquire = Mapping_acquire;
258 m_uno2java.release = Mapping_release;
259 m_uno2java.mapInterface = Mapping_map_to_java;
260 m_uno2java.m_bridge = this;
264 Bridge::~Bridge()
266 (*m_java_env->release)( m_java_env );
267 (*m_uno_env->aBase.release)( &m_uno_env->aBase );
270 JNI_info const * Bridge::getJniInfo() const {
271 return static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
272 ->info;
275 void JNI_context::java_exc_occurred() const
277 // !don't rely on JNI_info!
279 JLocalAutoRef jo_exc( *this, m_env->ExceptionOccurred() );
280 m_env->ExceptionClear();
281 assert(jo_exc.is());
282 if (! jo_exc.is())
284 throw BridgeRuntimeError(
285 "java exception occurred, but not available!?" +
286 get_stack_trace() );
289 // call toString(); don't rely on m_jni_info
290 jclass jo_class = m_env->FindClass( "java/lang/Object" );
291 if (m_env->ExceptionCheck())
293 m_env->ExceptionClear();
294 throw BridgeRuntimeError(
295 "cannot get class java.lang.Object!" + get_stack_trace() );
297 JLocalAutoRef jo_Object( *this, jo_class );
298 // method Object.toString()
299 jmethodID method_Object_toString = m_env->GetMethodID(
300 static_cast<jclass>(jo_Object.get()), "toString", "()Ljava/lang/String;" );
301 if (m_env->ExceptionCheck())
303 m_env->ExceptionClear();
304 throw BridgeRuntimeError(
305 "cannot get method id of java.lang.Object.toString()!" +
306 get_stack_trace() );
308 assert(method_Object_toString != 0);
310 JLocalAutoRef jo_descr(
311 *this, m_env->CallObjectMethodA(
312 jo_exc.get(), method_Object_toString, 0 ) );
313 if (m_env->ExceptionCheck()) // no chance at all
315 m_env->ExceptionClear();
316 throw BridgeRuntimeError(
317 "error examining java exception object!" +
318 get_stack_trace() );
321 jsize len = m_env->GetStringLength( static_cast<jstring>(jo_descr.get()) );
322 std::unique_ptr< rtl_mem > ustr_mem(
323 rtl_mem::allocate(
324 sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
325 rtl_uString * ustr = reinterpret_cast<rtl_uString *>(ustr_mem.get());
326 m_env->GetStringRegion( static_cast<jstring>(jo_descr.get()), 0, len, ustr->buffer );
327 if (m_env->ExceptionCheck())
329 m_env->ExceptionClear();
330 throw BridgeRuntimeError(
331 "invalid java string object!" + get_stack_trace() );
333 ustr->refCount = 1;
334 ustr->length = len;
335 ustr->buffer[ len ] = '\0';
336 OUString message( reinterpret_cast<rtl_uString *>(ustr_mem.release()), SAL_NO_ACQUIRE );
338 throw BridgeRuntimeError( message + get_stack_trace( jo_exc.get() ) );
342 void JNI_context::getClassForName(
343 jclass * classClass, jmethodID * methodForName) const
345 jclass c = m_env->FindClass("java/lang/Class");
346 if (c != 0) {
347 *methodForName = m_env->GetStaticMethodID(
348 c, "forName",
349 "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
351 *classClass = c;
355 jclass JNI_context::findClass(
356 char const * name, jclass classClass, jmethodID methodForName,
357 bool inException) const
359 jclass c = 0;
360 JLocalAutoRef s(*this, m_env->NewStringUTF(name));
361 if (s.is()) {
362 jvalue a[3];
363 a[0].l = s.get();
364 a[1].z = JNI_FALSE;
365 a[2].l = m_class_loader;
366 c = static_cast< jclass >(
367 m_env->CallStaticObjectMethodA(classClass, methodForName, a));
369 if (!inException) {
370 ensure_no_exception();
372 return c;
376 OUString JNI_context::get_stack_trace( jobject jo_exc ) const
378 JLocalAutoRef jo_JNI_proxy(
379 *this,
380 find_class( *this, "com.sun.star.bridges.jni_uno.JNI_proxy", true ) );
381 if (assert_no_exception())
383 // static method JNI_proxy.get_stack_trace()
384 jmethodID method = m_env->GetStaticMethodID(
385 static_cast<jclass>(jo_JNI_proxy.get()), "get_stack_trace",
386 "(Ljava/lang/Throwable;)Ljava/lang/String;" );
387 if (assert_no_exception() && (0 != method))
389 jvalue arg;
390 arg.l = jo_exc;
391 JLocalAutoRef jo_stack_trace(
392 *this, m_env->CallStaticObjectMethodA(
393 static_cast<jclass>(jo_JNI_proxy.get()), method, &arg ) );
394 if (assert_no_exception())
396 jsize len =
397 m_env->GetStringLength( static_cast<jstring>(jo_stack_trace.get()) );
398 std::unique_ptr< rtl_mem > ustr_mem(
399 rtl_mem::allocate(
400 sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
401 rtl_uString * ustr = reinterpret_cast<rtl_uString *>(ustr_mem.get());
402 m_env->GetStringRegion(
403 static_cast<jstring>(jo_stack_trace.get()), 0, len, ustr->buffer );
404 if (assert_no_exception())
406 ustr->refCount = 1;
407 ustr->length = len;
408 ustr->buffer[ len ] = '\0';
409 return OUString(
410 reinterpret_cast<rtl_uString *>(ustr_mem.release()), SAL_NO_ACQUIRE );
415 return OUString();
420 using namespace ::jni_uno;
422 extern "C" {
424 void SAL_CALL java_env_dispose(uno_Environment * env) {
425 auto * envData
426 = static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
427 if (envData != nullptr) {
428 jobject async;
430 osl::MutexGuard g(envData->mutex);
431 async = envData->asynchronousFinalizer;
432 envData->asynchronousFinalizer = nullptr;
434 if (async != nullptr) {
435 try {
436 JNI_guarded_context jni(envData->info, envData->machine);
437 jni->CallObjectMethodA(
438 async, envData->info->m_method_AsynchronousFinalizer_drain,
439 nullptr);
440 jni.ensure_no_exception();
441 jni->DeleteGlobalRef(async);
442 } catch (const BridgeRuntimeError & e) {
443 SAL_WARN(
444 "bridges",
445 "ignoring BridgeRuntimeError \"" << e.m_message << "\"");
446 } catch (
447 jvmaccess::VirtualMachine::AttachGuard::CreationException &)
449 SAL_WARN(
450 "bridges",
451 ("ignoring jvmaccess::VirtualMachine::AttachGuard"
452 "::CreationException"));
458 void SAL_CALL java_env_disposing(uno_Environment * env) {
459 java_env_dispose(env);
460 delete static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
463 #ifdef DISABLE_DYNLOADING
464 #define uno_initEnvironment java_uno_initEnvironment
465 #endif
468 SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_initEnvironment( uno_Environment * java_env )
469 SAL_THROW_EXTERN_C()
471 try {
472 // JavaComponentLoader::getJavaLoader (in
473 // stoc/source/javaloader/javaloader.cxx) stores a
474 // jvmaccess::UnoVirtualMachine pointer into java_env->pContext; replace
475 // it here with either a pointer to a full JniUnoEnvironmentData upon
476 // success, or with a null pointer upon failure (as this function cannot
477 // directly report back failure, so it uses that way to indirectly
478 // report failure later from within the Bridge ctor):
479 rtl::Reference<jvmaccess::UnoVirtualMachine> vm(
480 static_cast<jvmaccess::UnoVirtualMachine *>(java_env->pContext));
481 java_env->pContext = nullptr;
482 java_env->dispose = java_env_dispose;
483 java_env->environmentDisposing = java_env_disposing;
484 java_env->pExtEnv = 0; // no extended support
485 std::unique_ptr<jni_uno::JniUnoEnvironmentData> envData(
486 new jni_uno::JniUnoEnvironmentData(vm));
488 JNI_guarded_context jni(envData->info, envData->machine);
489 JLocalAutoRef ref(
490 jni,
491 jni->NewObject(
492 envData->info->m_class_AsynchronousFinalizer,
493 envData->info->m_ctor_AsynchronousFinalizer));
494 jni.ensure_no_exception();
495 envData->asynchronousFinalizer = jni->NewGlobalRef(ref.get());
496 jni.ensure_no_exception();
498 java_env->pContext = envData.release();
499 } catch (const BridgeRuntimeError & e) {
500 SAL_WARN("bridges", "BridgeRuntimeError \"" << e.m_message << "\"");
501 } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) {
502 SAL_WARN(
503 "bridges",
504 "jvmaccess::VirtualMachine::AttachGuard::CreationException");
508 #ifdef DISABLE_DYNLOADING
509 #define uno_ext_getMapping java_uno_ext_getMapping
510 #endif
513 SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_ext_getMapping(
514 uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo )
515 SAL_THROW_EXTERN_C()
517 assert(ppMapping != 0);
518 assert(pFrom != 0);
519 assert(pTo != 0);
520 if (0 != *ppMapping)
522 (*(*ppMapping)->release)( *ppMapping );
523 *ppMapping = 0;
526 static_assert(int(JNI_FALSE) == int(sal_False), "must be equal");
527 static_assert(int(JNI_TRUE) == int(sal_True), "must be equal");
528 static_assert(sizeof (jboolean) == sizeof (sal_Bool), "must be the same size");
529 static_assert(sizeof (jchar) == sizeof (sal_Unicode), "must be the same size");
530 static_assert(sizeof (jdouble) == sizeof (double), "must be the same size");
531 static_assert(sizeof (jfloat) == sizeof (float), "must be the same size");
532 static_assert(sizeof (jbyte) == sizeof (sal_Int8), "must be the same size");
533 static_assert(sizeof (jshort) == sizeof (sal_Int16), "must be the same size");
534 static_assert(sizeof (jint) == sizeof (sal_Int32), "must be the same size");
535 static_assert(sizeof (jlong) == sizeof (sal_Int64), "must be the same size");
537 OUString const & from_env_typename =
538 OUString::unacquired( &pFrom->pTypeName );
539 OUString const & to_env_typename =
540 OUString::unacquired( &pTo->pTypeName );
542 uno_Mapping * mapping = 0;
546 if ( from_env_typename == UNO_LB_JAVA && to_env_typename == UNO_LB_UNO )
548 Bridge * bridge =
549 new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1
550 mapping = &bridge->m_java2uno;
551 uno_registerMapping(
552 &mapping, Bridge_free,
553 pFrom, &pTo->pExtEnv->aBase, 0 );
554 // coverity[leaked_storage]
556 else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_JAVA )
558 Bridge * bridge =
559 new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1
560 mapping = &bridge->m_uno2java;
561 uno_registerMapping(
562 &mapping, Bridge_free,
563 &pFrom->pExtEnv->aBase, pTo, 0 );
564 // coverity[leaked_storage]
567 catch (const BridgeRuntimeError & err)
569 SAL_WARN("bridges", "BridgeRuntimeError \"" << err.m_message << "\"");
572 *ppMapping = mapping;
577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */