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 
34 using namespace ::osl;
35 using namespace ::jni_uno;
36 
37 namespace
38 {
39 extern "C"
40 {
41 
42 
43 void Mapping_acquire( uno_Mapping * mapping )
45 {
46  Mapping const * that = static_cast< Mapping const * >( mapping );
47  that->m_bridge->acquire();
48 }
49 
50 
51 void Mapping_release( uno_Mapping * mapping )
53 {
54  Mapping const * that = static_cast< Mapping const * >( mapping );
55  that->m_bridge->release();
56 }
57 
58 
59 void Mapping_map_to_uno(
60  uno_Mapping * mapping, void ** ppOut,
61  void * pIn, typelib_InterfaceTypeDescription * td )
63 {
64  uno_Interface ** ppUnoI = reinterpret_cast<uno_Interface **>(ppOut);
65  jobject javaI = static_cast<jobject>(pIn);
66 
67  static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
68  assert(ppUnoI != nullptr);
69  assert(td != nullptr);
70 
71  if (javaI == nullptr)
72  {
73  if (*ppUnoI != nullptr)
74  {
75  uno_Interface * p = *ppUnoI;
76  (*p->release)( p );
77  *ppUnoI = nullptr;
78  }
79  }
80  else
81  {
82  try
83  {
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));
91 
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)
98  {
99  uno_Interface * p = *ppUnoI;
100  (*p->release)( p );
101  }
102  *ppUnoI = pUnoI;
103  }
104  catch (const BridgeRuntimeError & err)
105  {
106  SAL_WARN(
107  "bridges",
108  "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
109  }
110  catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
111  {
112  SAL_WARN("bridges", "attaching current thread to java failed");
113  }
114  }
115 }
116 
117 
118 void Mapping_map_to_java(
119  uno_Mapping * mapping, void ** ppOut,
120  void * pIn, typelib_InterfaceTypeDescription * td )
122 {
123  jobject * ppJavaI = reinterpret_cast<jobject *>(ppOut);
124  uno_Interface * pUnoI = static_cast<uno_Interface *>(pIn);
125 
126  static_assert(sizeof (void *) == sizeof (jobject), "must be the same size");
127  assert(ppJavaI != nullptr);
128  assert(td != nullptr);
129 
130  try
131  {
132  if (pUnoI == nullptr)
133  {
134  if (*ppJavaI != nullptr)
135  {
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;
145  }
146  }
147  else
148  {
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));
156 
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 );
166  }
167  }
168  catch (const BridgeRuntimeError & err)
169  {
170  SAL_WARN(
171  "bridges",
172  "ignoring BridgeRuntimeError \"" << err.m_message << "\"");
173  }
174  catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
175  {
176  SAL_WARN("bridges", "attaching current thread to java failed");
177  }
178 }
179 
180 
181 void Bridge_free( uno_Mapping * mapping )
183 {
184  Mapping * that = static_cast< Mapping * >( mapping );
185  delete that->m_bridge;
186 }
187 
188 }
189 
190 }
191 
192 namespace jni_uno
193 {
194 
195 
196 void Bridge::acquire() const
197 {
198  if (++m_ref != 1)
199  return;
200 
202  {
203  uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
205  &mapping, Bridge_free,
206  m_java_env, &m_uno_env->aBase, nullptr );
207  }
208  else
209  {
210  uno_Mapping * mapping = const_cast< Mapping * >( &m_uno2java );
212  &mapping, Bridge_free,
213  &m_uno_env->aBase, m_java_env, nullptr );
214  }
215 }
216 
217 
218 void Bridge::release() const
219 {
220  if (! --m_ref )
221  {
224  ? const_cast< Mapping * >( &m_java2uno )
225  : const_cast< Mapping * >( &m_uno2java ) );
226  }
227 }
228 
229 
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 )
237 {
238  assert(m_java_env != nullptr);
239  assert(m_uno_env != nullptr);
240 
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)
245  {
246  throw BridgeRuntimeError("error during JNI-UNO's uno_initEnvironment");
247  }
248 
249  (*m_uno_env->aBase.acquire)( &m_uno_env->aBase );
250  (*m_java_env->acquire)( m_java_env );
251 
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;
262 }
263 
264 
266 {
267  (*m_java_env->release)( m_java_env );
268  (*m_uno_env->aBase.release)( &m_uno_env->aBase );
269 }
270 
271 JNI_info const * Bridge::getJniInfo() const {
272  return static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
273  ->info;
274 }
275 
277 {
278  // !don't rely on JNI_info!
279 
280  JLocalAutoRef jo_exc( *this, m_env->ExceptionOccurred() );
281  m_env->ExceptionClear();
282  assert(jo_exc.is());
283  if (! jo_exc.is())
284  {
285  throw BridgeRuntimeError(
286  "java exception occurred, but not available!?" +
287  get_stack_trace() );
288  }
289 
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())
293  {
294  m_env->ExceptionClear();
295  throw BridgeRuntimeError(
296  "cannot get class java.lang.Object!" + get_stack_trace() );
297  }
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())
303  {
304  m_env->ExceptionClear();
305  throw BridgeRuntimeError(
306  "cannot get method id of java.lang.Object.toString()!" +
307  get_stack_trace() );
308  }
309  assert(method_Object_toString != nullptr);
310 
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
315  {
316  m_env->ExceptionClear();
317  throw BridgeRuntimeError(
318  "error examining java exception object!" +
319  get_stack_trace() );
320  }
321 
322  jsize len = m_env->GetStringLength( static_cast<jstring>(jo_descr.get()) );
323  std::unique_ptr< rtl_mem > ustr_mem(
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())
329  {
330  m_env->ExceptionClear();
331  throw BridgeRuntimeError(
332  "invalid java string object!" + get_stack_trace() );
333  }
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 );
338 
339  throw BridgeRuntimeError( message + get_stack_trace( jo_exc.get() ) );
340 }
341 
342 
344  jclass * classClass, jmethodID * methodForName) const
345 {
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;");
351  }
352  *classClass = c;
353 }
354 
355 
357  char const * name, jclass classClass, jmethodID methodForName,
358  bool inException) const
359 {
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));
369  }
370  if (!inException) {
372  }
373  return c;
374 }
375 
376 
377 OUString JNI_context::get_stack_trace( jobject jo_exc ) const
378 {
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())
383  {
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))
389  {
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())
396  {
397  jsize len =
398  m_env->GetStringLength( static_cast<jstring>(jo_stack_trace.get()) );
399  std::unique_ptr< rtl_mem > ustr_mem(
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())
406  {
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 );
412  }
413  }
414  }
415  }
416  return OUString();
417 }
418 
419 }
420 
421 using namespace ::jni_uno;
422 
423 extern "C" {
424 
425 static void java_env_dispose(uno_Environment * env) {
426  auto * envData
427  = static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
428  if (envData == nullptr) return;
429 
430  jobject async;
431  {
432  osl::MutexGuard g(envData->mutex);
433  async = envData->asynchronousFinalizer;
434  envData->asynchronousFinalizer = nullptr;
435  }
436  if (async == nullptr) return;
437 
438  try {
439  JNI_guarded_context jni(envData->info, envData->machine);
440  jni->CallObjectMethodA(
441  async, envData->info->m_method_AsynchronousFinalizer_drain,
442  nullptr);
443  jni.ensure_no_exception();
444  jni->DeleteGlobalRef(async);
445  } catch (const BridgeRuntimeError & e) {
446  SAL_WARN(
447  "bridges",
448  "ignoring BridgeRuntimeError \"" << e.m_message << "\"");
449  } catch (
451  {
452  SAL_WARN(
453  "bridges",
454  ("ignoring jvmaccess::VirtualMachine::AttachGuard"
455  "::CreationException"));
456  }
457 }
458 
460  java_env_dispose(env);
461  delete static_cast<jni_uno::JniUnoEnvironmentData *>(env->pContext);
462 }
463 
464 #ifdef DISABLE_DYNLOADING
465 #define uno_initEnvironment java_uno_initEnvironment
466 #endif
467 
468 
469 SAL_DLLPUBLIC_EXPORT void uno_initEnvironment( uno_Environment * java_env )
471 {
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):
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(
488  {
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();
498  }
499  java_env->pContext = envData.release();
500  } catch (const BridgeRuntimeError & e) {
501  SAL_WARN("bridges", "BridgeRuntimeError \"" << e.m_message << "\"");
503  SAL_WARN(
504  "bridges",
505  "jvmaccess::VirtualMachine::AttachGuard::CreationException");
506  }
507 }
508 
509 #ifdef DISABLE_DYNLOADING
510 #define uno_ext_getMapping java_uno_ext_getMapping
511 #endif
512 
513 
514 SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(
515  uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo )
517 {
518  assert(ppMapping != nullptr);
519  assert(pFrom != nullptr);
520  assert(pTo != nullptr);
521  if (*ppMapping != nullptr)
522  {
523  (*(*ppMapping)->release)( *ppMapping );
524  *ppMapping = nullptr;
525  }
526 
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");
537 
538  OUString const & from_env_typename =
539  OUString::unacquired( &pFrom->pTypeName );
540  OUString const & to_env_typename =
541  OUString::unacquired( &pTo->pTypeName );
542 
543  uno_Mapping * mapping = nullptr;
544 
545  try
546  {
547  if ( from_env_typename == UNO_LB_JAVA && to_env_typename == UNO_LB_UNO )
548  {
549  Bridge * bridge =
550  new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1
551  mapping = &bridge->m_java2uno;
553  &mapping, Bridge_free,
554  pFrom, &pTo->pExtEnv->aBase, nullptr );
555  // coverity[leaked_storage] - on purpose
556  }
557  else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_JAVA )
558  {
559  Bridge * bridge =
560  new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1
561  mapping = &bridge->m_uno2java;
563  &mapping, Bridge_free,
564  &pFrom->pExtEnv->aBase, pTo, nullptr );
565  // coverity[leaked_storage] - on purpose
566  }
567  }
568  catch (const BridgeRuntimeError & err)
569  {
570  SAL_WARN("bridges", "BridgeRuntimeError \"" << err.m_message << "\"");
571  }
572 
573  *ppMapping = mapping;
574 }
575 
576 }
577 
578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
struct _uno_Environment uno_Environment
void java_exc_occurred() const
Definition: jni_bridge.cxx:276
Bridge(uno_Environment *java_env, uno_ExtEnvironment *uno_env, bool registered_java2uno)
Definition: jni_bridge.cxx:230
signed char sal_Int8
struct _uno_Mapping uno_Mapping
Definition: msvc/except.hxx:32
void acquire() const
Definition: jni_bridge.cxx:196
bool assert_no_exception() const
Definition: jni_base.h:104
jobject m_class_loader
Definition: jni_base.h:58
void SAL_CALL Mapping_release(uno_Mapping *mapping) SAL_THROW_EXTERN_C()
Bridge const * m_bridge
jobject get() const
Definition: jni_base.h:155
#define SAL_THROW_EXTERN_C()
std::atomic< std::size_t > m_ref
SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(uno_Mapping **ppMapping, uno_Environment *pFrom, uno_Environment *pTo) SAL_THROW_EXTERN_C()
Definition: jni_bridge.cxx:514
sal_uInt16 sal_Unicode
OUString get_stack_trace(jobject jo_exc=nullptr) const
Definition: jni_bridge.cxx:377
void SAL_CALL uno_revokeMapping(uno_Mapping *pMapping) SAL_THROW_EXTERN_C()
void SAL_CALL Mapping_acquire(uno_Mapping *mapping) SAL_THROW_EXTERN_C()
void release() const
Definition: jni_bridge.cxx:218
void SAL_CALL Bridge_free(uno_Mapping *mapping) SAL_THROW_EXTERN_C()
Mapping m_uno2java
Definition: jni_bridge.h:58
std::atomic< std::size_t > m_ref
Definition: jni_bridge.h:52
err
void ensure_no_exception() const
Definition: jni_base.h:96
uno_Any a
uno_ExtEnvironment * m_uno_env
Definition: jni_bridge.h:54
Bridge * m_bridge
Definition: jni_bridge.h:46
unsigned char sal_Bool
uno_Environment * m_java_env
Definition: jni_bridge.h:55
jclass findClass(char const *name, jclass classClass, jmethodID methodForName, bool inException) const
Definition: jni_bridge.cxx:356
bool m_registered_java2uno
Definition: jni_bridge.h:59
static void java_env_disposing(uno_Environment *env)
Definition: jni_bridge.cxx:459
static void java_env_dispose(uno_Environment *env)
Definition: jni_bridge.cxx:425
Mapping m_java2uno
Definition: jni_bridge.h:57
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 getClassForName(jclass *classClass, jmethodID *methodForName) const
Definition: jni_bridge.cxx:343
exports com.sun.star. bridge
void * p
#define SAL_WARN(area, stream)
SAL_DLLPUBLIC_EXPORT void uno_initEnvironment(uno_Environment *java_env) SAL_THROW_EXTERN_C()
Definition: jni_bridge.cxx:469
static rtl_mem * allocate(std::size_t bytes)
Definition: jni_base.h:218
JNI_info const * getJniInfo() const
Definition: jni_bridge.cxx:271
jclass find_class(JNI_context const &jni, char const *class_name, bool inException=false)
Definition: jni_helper.h:78
char const * name