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"
27 #include "jniunoenvironmentdata.hxx"
28 
29 #include <jvmaccess/unovirtualmachine.hxx>
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 
Mapping_acquire(uno_Mapping * mapping)43 void Mapping_acquire( uno_Mapping * mapping )
44     SAL_THROW_EXTERN_C()
45 {
46     Mapping const * that = static_cast< Mapping const * >( mapping );
47     that->m_bridge->acquire();
48 }
49 
50 
Mapping_release(uno_Mapping * mapping)51 void Mapping_release( uno_Mapping * mapping )
52     SAL_THROW_EXTERN_C()
53 {
54     Mapping const * that = static_cast< Mapping const * >( mapping );
55     that->m_bridge->release();
56 }
57 
58 
Mapping_map_to_uno(uno_Mapping * mapping,void ** ppOut,void * pIn,typelib_InterfaceTypeDescription * td)59 void Mapping_map_to_uno(
60     uno_Mapping * mapping, void ** ppOut,
61     void * pIn, typelib_InterfaceTypeDescription * td )
62     SAL_THROW_EXTERN_C()
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 
Mapping_map_to_java(uno_Mapping * mapping,void ** ppOut,void * pIn,typelib_InterfaceTypeDescription * td)118 void Mapping_map_to_java(
119     uno_Mapping * mapping, void ** ppOut,
120     void * pIn, typelib_InterfaceTypeDescription * td )
121     SAL_THROW_EXTERN_C()
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 
Bridge_free(uno_Mapping * mapping)181 void Bridge_free( uno_Mapping * mapping )
182     SAL_THROW_EXTERN_C()
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 
acquire() const196 void Bridge::acquire() const
197 {
198     if (++m_ref != 1)
199         return;
200 
201     if (m_registered_java2uno)
202     {
203         uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
204         uno_registerMapping(
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 );
211         uno_registerMapping(
212             &mapping, Bridge_free,
213             &m_uno_env->aBase, m_java_env, nullptr );
214     }
215 }
216 
217 
release() const218 void Bridge::release() const
219 {
220     if (! --m_ref )
221     {
222         uno_revokeMapping(
223             m_registered_java2uno
224             ? const_cast< Mapping * >( &m_java2uno )
225             : const_cast< Mapping * >( &m_uno2java ) );
226     }
227 }
228 
229 
Bridge(uno_Environment * java_env,uno_ExtEnvironment * uno_env,bool registered_java2uno)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 )
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 
~Bridge()265 Bridge::~Bridge()
266 {
267     (*m_java_env->release)( m_java_env );
268     (*m_uno_env->aBase.release)( &m_uno_env->aBase );
269 }
270 
getJniInfo() const271 JNI_info const * Bridge::getJniInfo() const {
272     return static_cast<jni_uno::JniUnoEnvironmentData *>(m_java_env->pContext)
273         ->info;
274 }
275 
java_exc_occurred() const276 void JNI_context::java_exc_occurred() const
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(
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())
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 
getClassForName(jclass * classClass,jmethodID * methodForName) const343 void JNI_context::getClassForName(
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 
findClass(char const * name,jclass classClass,jmethodID methodForName,bool inException) const356 jclass JNI_context::findClass(
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) {
371         ensure_no_exception();
372     }
373     return c;
374 }
375 
376 
get_stack_trace(jobject jo_exc) const377 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(
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())
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 
java_env_dispose(uno_Environment * env)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 (
450         jvmaccess::VirtualMachine::AttachGuard::CreationException &)
451     {
452         SAL_WARN(
453             "bridges",
454             ("ignoring jvmaccess::VirtualMachine::AttachGuard"
455              "::CreationException"));
456     }
457 }
458 
java_env_disposing(uno_Environment * env)459 static void java_env_disposing(uno_Environment * env) {
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 
uno_initEnvironment(uno_Environment * java_env)469 SAL_DLLPUBLIC_EXPORT void uno_initEnvironment( uno_Environment * java_env )
470     SAL_THROW_EXTERN_C()
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):
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));
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 << "\"");
502     } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) {
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 
uno_ext_getMapping(uno_Mapping ** ppMapping,uno_Environment * pFrom,uno_Environment * pTo)514 SAL_DLLPUBLIC_EXPORT void uno_ext_getMapping(
515     uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo )
516     SAL_THROW_EXTERN_C()
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;
552             uno_registerMapping(
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;
562             uno_registerMapping(
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: */
579