LibreOffice Module bridges (master) 1
jni_bridge.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <cassert>
24#include <memory>
25
26#include "jni_bridge.h"
28
30#include <rtl/ref.hxx>
31#include <rtl/strbuf.hxx>
32#include <uno/lbnames.h>
33
34using namespace ::jni_uno;
35
36namespace
37{
38extern "C"
39{
40
41
42void Mapping_acquire( uno_Mapping * mapping )
44{
45 Mapping const * that = static_cast< Mapping const * >( mapping );
46 that->m_bridge->acquire();
47}
48
49
50void Mapping_release( uno_Mapping * mapping )
52{
53 Mapping const * that = static_cast< Mapping const * >( mapping );
54 that->m_bridge->release();
55}
56
57
58void Mapping_map_to_uno(
59 uno_Mapping * mapping, void ** ppOut,
60 void * pIn, typelib_InterfaceTypeDescription * td )
62{
63 uno_Interface ** ppUnoI = reinterpret_cast<uno_Interface **>(ppOut);
64 jobject javaI = static_cast<jobject>(pIn);
65
66 static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
67 assert(ppUnoI != nullptr);
68 assert(td != nullptr);
69
70 if (javaI == nullptr)
71 {
72 if (*ppUnoI != nullptr)
73 {
74 uno_Interface * p = *ppUnoI;
75 (*p->release)( p );
76 *ppUnoI = nullptr;
77 }
78 }
79 else
80 {
81 try
82 {
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));
90
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 (*ppUnoI != nullptr)
97 {
98 uno_Interface * p = *ppUnoI;
99 (*p->release)( p );
100 }
101 *ppUnoI = pUnoI;
102 }
103 catch (const BridgeRuntimeError & err)
104 {
105 SAL_WARN(
106 "bridges",
107 "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
108 }
109 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
110 {
111 SAL_WARN("bridges", "attaching current thread to java failed");
112 }
113 }
114}
115
116
117void Mapping_map_to_java(
118 uno_Mapping * mapping, void ** ppOut,
119 void * pIn, typelib_InterfaceTypeDescription * td )
121{
122 jobject * ppJavaI = reinterpret_cast<jobject *>(ppOut);
123 uno_Interface * pUnoI = static_cast<uno_Interface *>(pIn);
124
125 static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
126 assert(ppJavaI != nullptr);
127 assert(td != nullptr);
128
129 try
130 {
131 if (pUnoI == nullptr)
132 {
133 if (*ppJavaI != nullptr)
134 {
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 = nullptr;
144 }
145 }
146 else
147 {
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));
155
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 (*ppJavaI != nullptr)
162 jni->DeleteGlobalRef( *ppJavaI );
163 *ppJavaI = jni->NewGlobalRef( jlocal );
164 jni->DeleteLocalRef( jlocal );
165 }
166 }
167 catch (const BridgeRuntimeError & err)
168 {
169 SAL_WARN(
170 "bridges",
171 "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
172 }
173 catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
174 {
175 SAL_WARN("bridges", "attaching current thread to java failed");
176 }
177}
178
179
180void Bridge_free( uno_Mapping * mapping )
182{
183 Mapping * that = static_cast< Mapping * >( mapping );
184 delete that->m_bridge;
185}
186
187}
188
189}
190
191namespace jni_uno
192{
193
194
195void Bridge::acquire() const
196{
197 if (++m_ref != 1)
198 return;
199
201 {
202 uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
204 &mapping, Bridge_free,
205 m_java_env, &m_uno_env->aBase, nullptr );
206 }
207 else
208 {
209 uno_Mapping * mapping = const_cast< Mapping * >( &m_uno2java );
211 &mapping, Bridge_free,
212 &m_uno_env->aBase, m_java_env, nullptr );
213 }
214}
215
216
217void Bridge::release() const
218{
219 if (! --m_ref )
220 {
223 ? const_cast< Mapping * >( &m_java2uno )
224 : const_cast< Mapping * >( &m_uno2java ) );
225 }
226}
227
228
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 )
236{
237 assert(m_java_env != nullptr);
238 assert(m_uno_env != nullptr);
239
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)
244 {
245 throw BridgeRuntimeError("error during JNI-UNO's uno_initEnvironment");
246 }
247
248 (*m_uno_env->aBase.acquire)( &m_uno_env->aBase );
249 (*m_java_env->acquire)( m_java_env );
250
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;
261}
262
263
265{
266 (*m_java_env->release)( m_java_env );
267 (*m_uno_env->aBase.release)( &m_uno_env->aBase );
268}
269
271 return static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
272 ->info;
273}
274
276{
277 // !don't rely on JNI_info!
278
279 JLocalAutoRef jo_exc( *this, m_env->ExceptionOccurred() );
280 m_env->ExceptionClear();
281 assert(jo_exc.is());
282 if (! jo_exc.is())
283 {
284 throw BridgeRuntimeError(
285 "java exception occurred, but not available!?" +
286 get_stack_trace() );
287 }
288
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())
292 {
293 m_env->ExceptionClear();
294 throw BridgeRuntimeError(
295 "cannot get class java.lang.Object!" + get_stack_trace() );
296 }
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())
302 {
303 m_env->ExceptionClear();
304 throw BridgeRuntimeError(
305 "cannot get method id of java.lang.Object.toString()!" +
306 get_stack_trace() );
307 }
308 assert(method_Object_toString != nullptr);
309
310 JLocalAutoRef jo_descr(
311 *this, m_env->CallObjectMethodA(
312 jo_exc.get(), method_Object_toString, nullptr ) );
313 if (m_env->ExceptionCheck()) // no chance at all
314 {
315 m_env->ExceptionClear();
316 throw BridgeRuntimeError(
317 "error examining java exception object!" +
318 get_stack_trace() );
319 }
320
321 jsize len = m_env->GetStringLength( static_cast<jstring>(jo_descr.get()) );
322 std::unique_ptr< rtl_mem > ustr_mem(
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, reinterpret_cast<jchar *>(ustr->buffer) );
327 if (m_env->ExceptionCheck())
328 {
329 m_env->ExceptionClear();
330 throw BridgeRuntimeError(
331 "invalid java string object!" + get_stack_trace() );
332 }
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 );
337
338 throw BridgeRuntimeError( message + get_stack_trace( jo_exc.get() ) );
339}
340
341
343 jclass * classClass, jmethodID * methodForName) const
344{
345 jclass c = m_env->FindClass("java/lang/Class");
346 if (c != nullptr) {
347 *methodForName = m_env->GetStaticMethodID(
348 c, "forName",
349 "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
350 }
351 *classClass = c;
352}
353
354
356 char const * name, jclass classClass, jmethodID methodForName,
357 bool inException) const
358{
359 jclass c = nullptr;
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));
368 }
369 if (!inException) {
371 }
372 return c;
373}
374
375
376OUString JNI_context::get_stack_trace( jobject jo_exc ) const
377{
378 JLocalAutoRef jo_JNI_proxy(
379 *this,
380 find_class( *this, "com.sun.star.bridges.jni_uno.JNI_proxy", true ) );
382 {
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() && (method != nullptr))
388 {
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 ) );
395 {
396 jsize len =
397 m_env->GetStringLength( static_cast<jstring>(jo_stack_trace.get()) );
398 std::unique_ptr< rtl_mem > ustr_mem(
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, reinterpret_cast<jchar *>(ustr->buffer) );
405 {
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 );
411 }
412 }
413 }
414 }
415 return OUString();
416}
417
418}
419
420using namespace ::jni_uno;
421
422extern "C" {
423
425 auto * envData
426 = static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
427 if (envData == nullptr) return;
428
429 jobject async;
430 {
431 std::unique_lock g(envData->mutex);
432 async = envData->asynchronousFinalizer;
433 envData->asynchronousFinalizer = nullptr;
434 }
435 if (async == nullptr) return;
436
437 try {
438 JNI_guarded_context jni(envData->info, envData->machine);
439 jni->CallObjectMethodA(
440 async, envData->info->m_method_AsynchronousFinalizer_drain,
441 nullptr);
442 jni.ensure_no_exception();
443 jni->DeleteGlobalRef(async);
444 } catch (const BridgeRuntimeError & e) {
445 SAL_WARN(
446 "bridges",
447 "ignoring BridgeRuntimeError \"" << e.m_message << "\"");
448 } catch (
450 {
451 SAL_WARN(
452 "bridges",
453 ("ignoring jvmaccess::VirtualMachine::AttachGuard"
454 "::CreationException"));
455 }
456}
457
460 delete static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
461}
462
463#ifdef DISABLE_DYNLOADING
464#define uno_initEnvironment java_uno_initEnvironment
465#endif
466
467
468SAL_DLLPUBLIC_EXPORT void uno_initEnvironment( uno_Environment * java_env )
470{
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):
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 = nullptr; // no extended support
485 std::unique_ptr<jni_uno::JniUnoEnvironmentData> envData(
487 {
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();
497 }
498 java_env->pContext = envData.release();
499 } catch (const BridgeRuntimeError & e) {
500 SAL_WARN("bridges", "BridgeRuntimeError \"" << e.m_message << "\"");
502 SAL_WARN(
503 "bridges",
504 "jvmaccess::VirtualMachine::AttachGuard::CreationException");
505 }
506}
507
508#ifdef DISABLE_DYNLOADING
509#define uno_ext_getMapping java_uno_ext_getMapping
510#endif
511
512
513SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(
514 uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo )
516{
517 assert(ppMapping != nullptr);
518 assert(pFrom != nullptr);
519 assert(pTo != nullptr);
520 if (*ppMapping != nullptr)
521 {
522 (*(*ppMapping)->release)( *ppMapping );
523 *ppMapping = nullptr;
524 }
525
526 static_assert(int(JNI_FALSE) == int(false), "must be equal");
527 static_assert(int(JNI_TRUE) == int(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");
536
537 OUString const & from_env_typename =
538 OUString::unacquired( &pFrom->pTypeName );
539 OUString const & to_env_typename =
540 OUString::unacquired( &pTo->pTypeName );
541
542 uno_Mapping * mapping = nullptr;
543
544 try
545 {
546 if ( from_env_typename == UNO_LB_JAVA && to_env_typename == UNO_LB_UNO )
547 {
548 Bridge * bridge =
549 new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1
550 mapping = &bridge->m_java2uno;
552 &mapping, Bridge_free,
553 pFrom, &pTo->pExtEnv->aBase, nullptr );
554 // coverity[leaked_storage] - on purpose
555 }
556 else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_JAVA )
557 {
558 Bridge * bridge =
559 new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1
560 mapping = &bridge->m_uno2java;
562 &mapping, Bridge_free,
563 &pFrom->pExtEnv->aBase, pTo, nullptr );
564 // coverity[leaked_storage] - on purpose
565 }
566 }
567 catch (const BridgeRuntimeError & err)
568 {
569 SAL_WARN("bridges", "BridgeRuntimeError \"" << err.m_message << "\"");
570 }
571
572 *ppMapping = mapping;
573}
574
575}
576
577/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool is() const
Definition: jni_base.h:158
jobject get() const
Definition: jni_base.h:156
jclass findClass(char const *name, jclass classClass, jmethodID methodForName, bool inException) const
Definition: jni_bridge.cxx:355
bool assert_no_exception() const
Definition: jni_base.h:105
OUString get_stack_trace(jobject jo_exc=nullptr) const
Definition: jni_bridge.cxx:376
void java_exc_occurred() const
Definition: jni_bridge.cxx:275
void ensure_no_exception() const
Definition: jni_base.h:97
jobject m_class_loader
Definition: jni_base.h:59
void getClassForName(jclass *classClass, jmethodID *methodForName) const
Definition: jni_bridge.cxx:342
struct _uno_Environment uno_Environment
char const * name
SAL_DLLPUBLIC_EXPORT void uno_initEnvironment(uno_Environment *java_env) SAL_THROW_EXTERN_C()
Definition: jni_bridge.cxx:468
static void java_env_disposing(uno_Environment *env)
Definition: jni_bridge.cxx:458
SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(uno_Mapping **ppMapping, uno_Environment *pFrom, uno_Environment *pTo) SAL_THROW_EXTERN_C()
Definition: jni_bridge.cxx:513
static void java_env_dispose(uno_Environment *env)
Definition: jni_bridge.cxx:424
void * p
uno_Any a
std::atomic< std::size_t > m_ref
Bridge const * m_bridge
void SAL_CALL uno_registerMapping(uno_Mapping **ppMapping, uno_freeMappingFunc freeMapping, uno_Environment *pFrom, uno_Environment *pTo, rtl_uString *pAddPurpose) SAL_THROW_EXTERN_C()
void SAL_CALL uno_revokeMapping(uno_Mapping *pMapping) SAL_THROW_EXTERN_C()
#define SAL_WARN(area, stream)
const css::uno::Reference< css::xml::crypto::XSecurityEnvironment > & env
struct _uno_Mapping uno_Mapping
Definition: msvc/except.hxx:33
err
void SAL_CALL Mapping_acquire(uno_Mapping *mapping) SAL_THROW_EXTERN_C()
void SAL_CALL Mapping_release(uno_Mapping *mapping) SAL_THROW_EXTERN_C()
void SAL_CALL Bridge_free(uno_Mapping *mapping) SAL_THROW_EXTERN_C()
jclass find_class(JNI_context const &jni, char const *class_name, bool inException=false)
Definition: jni_helper.h:79
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
Mapping m_uno2java
Definition: jni_bridge.h:58
void release() const
Definition: jni_bridge.cxx:217
uno_ExtEnvironment * m_uno_env
Definition: jni_bridge.h:54
bool m_registered_java2uno
Definition: jni_bridge.h:59
Bridge(uno_Environment *java_env, uno_ExtEnvironment *uno_env, bool registered_java2uno)
Definition: jni_bridge.cxx:229
JNI_info const * getJniInfo() const
Definition: jni_bridge.cxx:270
std::atomic< std::size_t > m_ref
Definition: jni_bridge.h:52
void acquire() const
Definition: jni_bridge.cxx:195
Mapping m_java2uno
Definition: jni_bridge.h:57
uno_Environment * m_java_env
Definition: jni_bridge.h:55
rtl::Reference< jvmaccess::UnoVirtualMachine > const machine
Bridge * m_bridge
Definition: jni_bridge.h:46
static rtl_mem * allocate(std::size_t bytes)
Definition: jni_base.h:219
unsigned char sal_Bool
#define SAL_THROW_EXTERN_C()
sal_uInt16 sal_Unicode
signed char sal_Int8