bump product version to 6.3.0.0.beta1
[LibreOffice.git] / bridges / source / jni_uno / jni_bridge.cxx
blob6ff327a1eb060aca23896ff0a6e7db76badabe48
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>
21 #include <sal/log.hxx>
23 #include <cassert>
24 #include <memory>
26 #include "jni_bridge.h"
27 #include "jniunoenvironmentdata.hxx"
29 #include <jvmaccess/unovirtualmachine.hxx>
30 #include <rtl/ref.hxx>
31 #include <rtl/strbuf.hxx>
32 #include <uno/lbnames.h>
34 using namespace ::osl;
35 using namespace ::jni_uno;
37 namespace
39 extern "C"
43 void Mapping_acquire( uno_Mapping * mapping )
44 SAL_THROW_EXTERN_C()
46 Mapping const * that = static_cast< Mapping const * >( mapping );
47 that->m_bridge->acquire();
51 void Mapping_release( uno_Mapping * mapping )
52 SAL_THROW_EXTERN_C()
54 Mapping const * that = static_cast< Mapping const * >( mapping );
55 that->m_bridge->release();
59 void Mapping_map_to_uno(
60 uno_Mapping * mapping, void ** ppOut,
61 void * pIn, typelib_InterfaceTypeDescription * td )
62 SAL_THROW_EXTERN_C()
64 uno_Interface ** ppUnoI = reinterpret_cast<uno_Interface **>(ppOut);
65 jobject javaI = static_cast<jobject>(pIn);
67 static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
68 assert(ppUnoI != nullptr);
69 assert(td != nullptr);
71 if (javaI == nullptr)
73 if (*ppUnoI != nullptr)
75 uno_Interface * p = *ppUnoI;
76 (*p->release)( p );
77 *ppUnoI = nullptr;
80 else
82 try
84 Bridge const * bridge =
85 static_cast< Mapping const * >( mapping )->m_bridge;
86 JNI_guarded_context jni(
87 bridge->getJniInfo(),
88 (static_cast<jni_uno::JniUnoEnvironmentData *>(
89 bridge->m_java_env->pContext)
90 ->machine));
92 JNI_interface_type_info const * info =
93 static_cast< JNI_interface_type_info const * >(
94 bridge->getJniInfo()->get_type_info(
95 jni, &td->aBase ) );
96 uno_Interface * pUnoI = bridge->map_to_uno( jni, javaI, info );
97 if (*ppUnoI != nullptr)
99 uno_Interface * p = *ppUnoI;
100 (*p->release)( p );
102 *ppUnoI = pUnoI;
104 catch (const BridgeRuntimeError & err)
106 SAL_WARN(
107 "bridges",
108 "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
110 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
112 SAL_WARN("bridges", "attaching current thread to java failed");
118 void Mapping_map_to_java(
119 uno_Mapping * mapping, void ** ppOut,
120 void * pIn, typelib_InterfaceTypeDescription * td )
121 SAL_THROW_EXTERN_C()
123 jobject * ppJavaI = reinterpret_cast<jobject *>(ppOut);
124 uno_Interface * pUnoI = static_cast<uno_Interface *>(pIn);
126 static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
127 assert(ppJavaI != nullptr);
128 assert(td != nullptr);
132 if (pUnoI == nullptr)
134 if (*ppJavaI != nullptr)
136 Bridge const * bridge =
137 static_cast< Mapping const * >( mapping )->m_bridge;
138 JNI_guarded_context jni(
139 bridge->getJniInfo(),
140 (static_cast<jni_uno::JniUnoEnvironmentData *>(
141 bridge->m_java_env->pContext)
142 ->machine));
143 jni->DeleteGlobalRef( *ppJavaI );
144 *ppJavaI = nullptr;
147 else
149 Bridge const * bridge =
150 static_cast< Mapping const * >( mapping )->m_bridge;
151 JNI_guarded_context jni(
152 bridge->getJniInfo(),
153 (static_cast<jni_uno::JniUnoEnvironmentData *>(
154 bridge->m_java_env->pContext)
155 ->machine));
157 JNI_interface_type_info const * info =
158 static_cast< JNI_interface_type_info const * >(
159 bridge->getJniInfo()->get_type_info(
160 jni, &td->aBase ) );
161 jobject jlocal = bridge->map_to_java( jni, pUnoI, info );
162 if (*ppJavaI != nullptr)
163 jni->DeleteGlobalRef( *ppJavaI );
164 *ppJavaI = jni->NewGlobalRef( jlocal );
165 jni->DeleteLocalRef( jlocal );
168 catch (const BridgeRuntimeError & err)
170 SAL_WARN(
171 "bridges",
172 "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
174 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
176 SAL_WARN("bridges", "attaching current thread to java failed");
181 void Bridge_free( uno_Mapping * mapping )
182 SAL_THROW_EXTERN_C()
184 Mapping * that = static_cast< Mapping * >( mapping );
185 delete that->m_bridge;
192 namespace jni_uno
196 void Bridge::acquire() const
198 if (++m_ref == 1)
200 if (m_registered_java2uno)
202 uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
203 uno_registerMapping(
204 &mapping, Bridge_free,
205 m_java_env, &m_uno_env->aBase, nullptr );
207 else
209 uno_Mapping * mapping = const_cast< Mapping * >( &m_uno2java );
210 uno_registerMapping(
211 &mapping, Bridge_free,
212 &m_uno_env->aBase, m_java_env, nullptr );
218 void Bridge::release() const
220 if (! --m_ref )
222 uno_revokeMapping(
223 m_registered_java2uno
224 ? const_cast< Mapping * >( &m_java2uno )
225 : const_cast< Mapping * >( &m_uno2java ) );
230 Bridge::Bridge(
231 uno_Environment * java_env, uno_ExtEnvironment * uno_env,
232 bool registered_java2uno )
233 : m_ref( 1 ),
234 m_uno_env( uno_env ),
235 m_java_env( java_env ),
236 m_registered_java2uno( registered_java2uno )
238 assert(m_java_env != nullptr);
239 assert(m_uno_env != nullptr);
241 // uno_initEnvironment (below) cannot report errors directly, so it clears
242 // its pContext upon error to indirectly report errors from here:
243 if (static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
244 == nullptr)
246 throw BridgeRuntimeError("error during JNI-UNO's uno_initEnvironment");
249 (*m_uno_env->aBase.acquire)( &m_uno_env->aBase );
250 (*m_java_env->acquire)( m_java_env );
252 // java2uno
253 m_java2uno.acquire = Mapping_acquire;
254 m_java2uno.release = Mapping_release;
255 m_java2uno.mapInterface = Mapping_map_to_uno;
256 m_java2uno.m_bridge = this;
257 // uno2java
258 m_uno2java.acquire = Mapping_acquire;
259 m_uno2java.release = Mapping_release;
260 m_uno2java.mapInterface = Mapping_map_to_java;
261 m_uno2java.m_bridge = this;
265 Bridge::~Bridge()
267 (*m_java_env->release)( m_java_env );
268 (*m_uno_env->aBase.release)( &m_uno_env->aBase );
271 JNI_info const * Bridge::getJniInfo() const {
272 return static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
273 ->info;
276 void JNI_context::java_exc_occurred() const
278 // !don't rely on JNI_info!
280 JLocalAutoRef jo_exc( *this, m_env->ExceptionOccurred() );
281 m_env->ExceptionClear();
282 assert(jo_exc.is());
283 if (! jo_exc.is())
285 throw BridgeRuntimeError(
286 "java exception occurred, but not available!?" +
287 get_stack_trace() );
290 // call toString(); don't rely on m_jni_info
291 jclass jo_class = m_env->FindClass( "java/lang/Object" );
292 if (m_env->ExceptionCheck())
294 m_env->ExceptionClear();
295 throw BridgeRuntimeError(
296 "cannot get class java.lang.Object!" + get_stack_trace() );
298 JLocalAutoRef jo_Object( *this, jo_class );
299 // method Object.toString()
300 jmethodID method_Object_toString = m_env->GetMethodID(
301 static_cast<jclass>(jo_Object.get()), "toString", "()Ljava/lang/String;" );
302 if (m_env->ExceptionCheck())
304 m_env->ExceptionClear();
305 throw BridgeRuntimeError(
306 "cannot get method id of java.lang.Object.toString()!" +
307 get_stack_trace() );
309 assert(method_Object_toString != nullptr);
311 JLocalAutoRef jo_descr(
312 *this, m_env->CallObjectMethodA(
313 jo_exc.get(), method_Object_toString, nullptr ) );
314 if (m_env->ExceptionCheck()) // no chance at all
316 m_env->ExceptionClear();
317 throw BridgeRuntimeError(
318 "error examining java exception object!" +
319 get_stack_trace() );
322 jsize len = m_env->GetStringLength( static_cast<jstring>(jo_descr.get()) );
323 std::unique_ptr< rtl_mem > ustr_mem(
324 rtl_mem::allocate(
325 sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
326 rtl_uString * ustr = reinterpret_cast<rtl_uString *>(ustr_mem.get());
327 m_env->GetStringRegion( static_cast<jstring>(jo_descr.get()), 0, len, reinterpret_cast<jchar *>(ustr->buffer) );
328 if (m_env->ExceptionCheck())
330 m_env->ExceptionClear();
331 throw BridgeRuntimeError(
332 "invalid java string object!" + get_stack_trace() );
334 ustr->refCount = 1;
335 ustr->length = len;
336 ustr->buffer[ len ] = '\0';
337 OUString message( reinterpret_cast<rtl_uString *>(ustr_mem.release()), SAL_NO_ACQUIRE );
339 throw BridgeRuntimeError( message + get_stack_trace( jo_exc.get() ) );
343 void JNI_context::getClassForName(
344 jclass * classClass, jmethodID * methodForName) const
346 jclass c = m_env->FindClass("java/lang/Class");
347 if (c != nullptr) {
348 *methodForName = m_env->GetStaticMethodID(
349 c, "forName",
350 "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
352 *classClass = c;
356 jclass JNI_context::findClass(
357 char const * name, jclass classClass, jmethodID methodForName,
358 bool inException) const
360 jclass c = nullptr;
361 JLocalAutoRef s(*this, m_env->NewStringUTF(name));
362 if (s.is()) {
363 jvalue a[3];
364 a[0].l = s.get();
365 a[1].z = JNI_FALSE;
366 a[2].l = m_class_loader;
367 c = static_cast< jclass >(
368 m_env->CallStaticObjectMethodA(classClass, methodForName, a));
370 if (!inException) {
371 ensure_no_exception();
373 return c;
377 OUString JNI_context::get_stack_trace( jobject jo_exc ) const
379 JLocalAutoRef jo_JNI_proxy(
380 *this,
381 find_class( *this, "com.sun.star.bridges.jni_uno.JNI_proxy", true ) );
382 if (assert_no_exception())
384 // static method JNI_proxy.get_stack_trace()
385 jmethodID method = m_env->GetStaticMethodID(
386 static_cast<jclass>(jo_JNI_proxy.get()), "get_stack_trace",
387 "(Ljava/lang/Throwable;)Ljava/lang/String;" );
388 if (assert_no_exception() && (method != nullptr))
390 jvalue arg;
391 arg.l = jo_exc;
392 JLocalAutoRef jo_stack_trace(
393 *this, m_env->CallStaticObjectMethodA(
394 static_cast<jclass>(jo_JNI_proxy.get()), method, &arg ) );
395 if (assert_no_exception())
397 jsize len =
398 m_env->GetStringLength( static_cast<jstring>(jo_stack_trace.get()) );
399 std::unique_ptr< rtl_mem > ustr_mem(
400 rtl_mem::allocate(
401 sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
402 rtl_uString * ustr = reinterpret_cast<rtl_uString *>(ustr_mem.get());
403 m_env->GetStringRegion(
404 static_cast<jstring>(jo_stack_trace.get()), 0, len, reinterpret_cast<jchar *>(ustr->buffer) );
405 if (assert_no_exception())
407 ustr->refCount = 1;
408 ustr->length = len;
409 ustr->buffer[ len ] = '\0';
410 return OUString(
411 reinterpret_cast<rtl_uString *>(ustr_mem.release()), SAL_NO_ACQUIRE );
416 return OUString();
421 using namespace ::jni_uno;
423 extern "C" {
425 static void java_env_dispose(uno_Environment * env) {
426 auto * envData
427 = static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
428 if (envData != nullptr) {
429 jobject async;
431 osl::MutexGuard g(envData->mutex);
432 async = envData->asynchronousFinalizer;
433 envData->asynchronousFinalizer = nullptr;
435 if (async != nullptr) {
436 try {
437 JNI_guarded_context jni(envData->info, envData->machine);
438 jni->CallObjectMethodA(
439 async, envData->info->m_method_AsynchronousFinalizer_drain,
440 nullptr);
441 jni.ensure_no_exception();
442 jni->DeleteGlobalRef(async);
443 } catch (const BridgeRuntimeError & e) {
444 SAL_WARN(
445 "bridges",
446 "ignoring BridgeRuntimeError \"" << e.m_message << "\"");
447 } catch (
448 jvmaccess::VirtualMachine::AttachGuard::CreationException &)
450 SAL_WARN(
451 "bridges",
452 ("ignoring jvmaccess::VirtualMachine::AttachGuard"
453 "::CreationException"));
459 static void java_env_disposing(uno_Environment * env) {
460 java_env_dispose(env);
461 delete static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
464 #ifdef DISABLE_DYNLOADING
465 #define uno_initEnvironment java_uno_initEnvironment
466 #endif
469 SAL_DLLPUBLIC_EXPORT void uno_initEnvironment( uno_Environment * java_env )
470 SAL_THROW_EXTERN_C()
472 try {
473 // JavaComponentLoader::getJavaLoader (in
474 // stoc/source/javaloader/javaloader.cxx) stores a
475 // jvmaccess::UnoVirtualMachine pointer into java_env->pContext; replace
476 // it here with either a pointer to a full JniUnoEnvironmentData upon
477 // success, or with a null pointer upon failure (as this function cannot
478 // directly report back failure, so it uses that way to indirectly
479 // report failure later from within the Bridge ctor):
480 rtl::Reference<jvmaccess::UnoVirtualMachine> vm(
481 static_cast<jvmaccess::UnoVirtualMachine *>(java_env->pContext));
482 java_env->pContext = nullptr;
483 java_env->dispose = java_env_dispose;
484 java_env->environmentDisposing = java_env_disposing;
485 java_env->pExtEnv = nullptr; // no extended support
486 std::unique_ptr<jni_uno::JniUnoEnvironmentData> envData(
487 new jni_uno::JniUnoEnvironmentData(vm));
489 JNI_guarded_context jni(envData->info, envData->machine);
490 JLocalAutoRef ref(
491 jni,
492 jni->NewObject(
493 envData->info->m_class_AsynchronousFinalizer,
494 envData->info->m_ctor_AsynchronousFinalizer));
495 jni.ensure_no_exception();
496 envData->asynchronousFinalizer = jni->NewGlobalRef(ref.get());
497 jni.ensure_no_exception();
499 java_env->pContext = envData.release();
500 } catch (const BridgeRuntimeError & e) {
501 SAL_WARN("bridges", "BridgeRuntimeError \"" << e.m_message << "\"");
502 } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) {
503 SAL_WARN(
504 "bridges",
505 "jvmaccess::VirtualMachine::AttachGuard::CreationException");
509 #ifdef DISABLE_DYNLOADING
510 #define uno_ext_getMapping java_uno_ext_getMapping
511 #endif
514 SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(
515 uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo )
516 SAL_THROW_EXTERN_C()
518 assert(ppMapping != nullptr);
519 assert(pFrom != nullptr);
520 assert(pTo != nullptr);
521 if (*ppMapping != nullptr)
523 (*(*ppMapping)->release)( *ppMapping );
524 *ppMapping = nullptr;
527 static_assert(int(JNI_FALSE) == int(false), "must be equal");
528 static_assert(int(JNI_TRUE) == int(true), "must be equal");
529 static_assert(sizeof (jboolean) == sizeof (sal_Bool), "must be the same size");
530 static_assert(sizeof (jchar) == sizeof (sal_Unicode), "must be the same size");
531 static_assert(sizeof (jdouble) == sizeof (double), "must be the same size");
532 static_assert(sizeof (jfloat) == sizeof (float), "must be the same size");
533 static_assert(sizeof (jbyte) == sizeof (sal_Int8), "must be the same size");
534 static_assert(sizeof (jshort) == sizeof (sal_Int16), "must be the same size");
535 static_assert(sizeof (jint) == sizeof (sal_Int32), "must be the same size");
536 static_assert(sizeof (jlong) == sizeof (sal_Int64), "must be the same size");
538 OUString const & from_env_typename =
539 OUString::unacquired( &pFrom->pTypeName );
540 OUString const & to_env_typename =
541 OUString::unacquired( &pTo->pTypeName );
543 uno_Mapping * mapping = nullptr;
547 if ( from_env_typename == UNO_LB_JAVA && to_env_typename == UNO_LB_UNO )
549 Bridge * bridge =
550 new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1
551 mapping = &bridge->m_java2uno;
552 uno_registerMapping(
553 &mapping, Bridge_free,
554 pFrom, &pTo->pExtEnv->aBase, nullptr );
555 // coverity[leaked_storage] - on purpose
557 else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_JAVA )
559 Bridge * bridge =
560 new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1
561 mapping = &bridge->m_uno2java;
562 uno_registerMapping(
563 &mapping, Bridge_free,
564 &pFrom->pExtEnv->aBase, pTo, nullptr );
565 // coverity[leaked_storage] - on purpose
568 catch (const BridgeRuntimeError & err)
570 SAL_WARN("bridges", "BridgeRuntimeError \"" << err.m_message << "\"");
573 *ppMapping = mapping;
578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */