Line data Source code
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 :
22 : #include <cassert>
23 : #include <memory>
24 :
25 : #include "jni_bridge.h"
26 :
27 : #include <boost/static_assert.hpp>
28 : #include "jvmaccess/unovirtualmachine.hxx"
29 : #include "rtl/ref.hxx"
30 : #include "rtl/strbuf.hxx"
31 : #include "uno/lbnames.h"
32 :
33 : using namespace ::osl;
34 : using namespace ::jni_uno;
35 :
36 : namespace
37 : {
38 : extern "C"
39 : {
40 :
41 :
42 12 : void SAL_CALL Mapping_acquire( uno_Mapping * mapping )
43 : SAL_THROW_EXTERN_C()
44 : {
45 12 : Mapping const * that = static_cast< Mapping const * >( mapping );
46 12 : that->m_bridge->acquire();
47 12 : }
48 :
49 :
50 18 : void SAL_CALL Mapping_release( uno_Mapping * mapping )
51 : SAL_THROW_EXTERN_C()
52 : {
53 18 : Mapping const * that = static_cast< Mapping const * >( mapping );
54 18 : that->m_bridge->release();
55 18 : }
56 :
57 :
58 6 : void SAL_CALL Mapping_map_to_uno(
59 : uno_Mapping * mapping, void ** ppOut,
60 : void * pIn, typelib_InterfaceTypeDescription * td )
61 : SAL_THROW_EXTERN_C()
62 : {
63 6 : uno_Interface ** ppUnoI = (uno_Interface **)ppOut;
64 6 : jobject javaI = (jobject) pIn;
65 :
66 : BOOST_STATIC_ASSERT( sizeof (void *) == sizeof (jobject) );
67 : assert(ppUnoI != 0);
68 : assert(td != 0);
69 :
70 6 : if (0 == javaI)
71 : {
72 0 : if (0 != *ppUnoI)
73 : {
74 0 : uno_Interface * p = *(uno_Interface **)ppUnoI;
75 0 : (*p->release)( p );
76 0 : *ppUnoI = 0;
77 : }
78 : }
79 : else
80 : {
81 : try
82 : {
83 : Bridge const * bridge =
84 6 : static_cast< Mapping const * >( mapping )->m_bridge;
85 : JNI_guarded_context jni(
86 : bridge->m_jni_info,
87 : reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
88 6 : bridge->m_java_env->pContext ) );
89 :
90 : JNI_interface_type_info const * info =
91 : static_cast< JNI_interface_type_info const * >(
92 : bridge->m_jni_info->get_type_info(
93 6 : jni, (typelib_TypeDescription *)td ) );
94 6 : uno_Interface * pUnoI = bridge->map_to_uno( jni, javaI, info );
95 6 : if (0 != *ppUnoI)
96 : {
97 0 : uno_Interface * p = *(uno_Interface **)ppUnoI;
98 0 : (*p->release)( p );
99 : }
100 6 : *ppUnoI = pUnoI;
101 : }
102 0 : catch (const BridgeRuntimeError & err)
103 : {
104 : SAL_WARN(
105 : "bridges",
106 : "ingoring BridgeRuntimeError \"" << err.m_message << "\"");
107 : }
108 0 : catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
109 : {
110 : SAL_WARN("bridges", "attaching current thread to java failed");
111 : }
112 : }
113 6 : }
114 :
115 :
116 0 : void SAL_CALL Mapping_map_to_java(
117 : uno_Mapping * mapping, void ** ppOut,
118 : void * pIn, typelib_InterfaceTypeDescription * td )
119 : SAL_THROW_EXTERN_C()
120 : {
121 0 : jobject * ppJavaI = (jobject *) ppOut;
122 0 : uno_Interface * pUnoI = (uno_Interface *)pIn;
123 :
124 : BOOST_STATIC_ASSERT( sizeof (void *) == sizeof (jobject) );
125 : assert(ppJavaI != 0);
126 : assert(td != 0);
127 :
128 : try
129 : {
130 0 : if (0 == pUnoI)
131 : {
132 0 : if (0 != *ppJavaI)
133 : {
134 : Bridge const * bridge =
135 0 : static_cast< Mapping const * >( mapping )->m_bridge;
136 : JNI_guarded_context jni(
137 : bridge->m_jni_info,
138 : reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
139 0 : bridge->m_java_env->pContext ) );
140 0 : jni->DeleteGlobalRef( *ppJavaI );
141 0 : *ppJavaI = 0;
142 : }
143 : }
144 : else
145 : {
146 : Bridge const * bridge =
147 0 : static_cast< Mapping const * >( mapping )->m_bridge;
148 : JNI_guarded_context jni(
149 : bridge->m_jni_info,
150 : reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
151 0 : bridge->m_java_env->pContext ) );
152 :
153 : JNI_interface_type_info const * info =
154 : static_cast< JNI_interface_type_info const * >(
155 : bridge->m_jni_info->get_type_info(
156 0 : jni, (typelib_TypeDescription *)td ) );
157 0 : jobject jlocal = bridge->map_to_java( jni, pUnoI, info );
158 0 : if (0 != *ppJavaI)
159 0 : jni->DeleteGlobalRef( *ppJavaI );
160 0 : *ppJavaI = jni->NewGlobalRef( jlocal );
161 0 : jni->DeleteLocalRef( jlocal );
162 : }
163 : }
164 0 : catch (const BridgeRuntimeError & err)
165 : {
166 : SAL_WARN(
167 : "bridges",
168 : "ingoring BridgeRuntimeError \"" << err.m_message << "\"");
169 : }
170 0 : catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
171 : {
172 : SAL_WARN("bridges", "attaching current thread to java failed");
173 : }
174 0 : }
175 :
176 :
177 0 : void SAL_CALL Bridge_free( uno_Mapping * mapping )
178 : SAL_THROW_EXTERN_C()
179 : {
180 0 : Mapping * that = static_cast< Mapping * >( mapping );
181 0 : delete that->m_bridge;
182 0 : }
183 :
184 : }
185 :
186 : }
187 :
188 : namespace jni_uno
189 : {
190 :
191 :
192 126 : void Bridge::acquire() const
193 : {
194 126 : if (1 == osl_atomic_increment( &m_ref ))
195 : {
196 0 : if (m_registered_java2uno)
197 : {
198 0 : uno_Mapping * mapping = const_cast< Mapping * >( &m_java2uno );
199 : uno_registerMapping(
200 : &mapping, Bridge_free,
201 0 : m_java_env, (uno_Environment *)m_uno_env, 0 );
202 : }
203 : else
204 : {
205 0 : uno_Mapping * mapping = const_cast< Mapping * >( &m_uno2java );
206 : uno_registerMapping(
207 : &mapping, Bridge_free,
208 0 : (uno_Environment *)m_uno_env, m_java_env, 0 );
209 : }
210 : }
211 126 : }
212 :
213 :
214 68 : void Bridge::release() const
215 : {
216 68 : if (! osl_atomic_decrement( &m_ref ))
217 : {
218 : uno_revokeMapping(
219 : m_registered_java2uno
220 : ? const_cast< Mapping * >( &m_java2uno )
221 0 : : const_cast< Mapping * >( &m_uno2java ) );
222 : }
223 68 : }
224 :
225 :
226 6 : Bridge::Bridge(
227 : uno_Environment * java_env, uno_ExtEnvironment * uno_env,
228 : bool registered_java2uno )
229 : : m_ref( 1 ),
230 : m_uno_env( uno_env ),
231 : m_java_env( java_env ),
232 6 : m_registered_java2uno( registered_java2uno )
233 : {
234 : // bootstrapping bridge jni_info
235 : m_jni_info = JNI_info::get_jni_info(
236 : reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
237 6 : m_java_env->pContext ) );
238 :
239 : assert(m_java_env != 0);
240 : assert(m_uno_env != 0);
241 6 : (*((uno_Environment *)m_uno_env)->acquire)( (uno_Environment *)m_uno_env );
242 6 : (*m_java_env->acquire)( m_java_env );
243 :
244 : // java2uno
245 6 : m_java2uno.acquire = Mapping_acquire;
246 6 : m_java2uno.release = Mapping_release;
247 6 : m_java2uno.mapInterface = Mapping_map_to_uno;
248 6 : m_java2uno.m_bridge = this;
249 : // uno2java
250 6 : m_uno2java.acquire = Mapping_acquire;
251 6 : m_uno2java.release = Mapping_release;
252 6 : m_uno2java.mapInterface = Mapping_map_to_java;
253 6 : m_uno2java.m_bridge = this;
254 6 : }
255 :
256 :
257 0 : Bridge::~Bridge()
258 : {
259 0 : (*m_java_env->release)( m_java_env );
260 0 : (*((uno_Environment *)m_uno_env)->release)( (uno_Environment *)m_uno_env );
261 0 : }
262 :
263 :
264 :
265 0 : void JNI_context::java_exc_occurred() const
266 : {
267 : // !don't rely on JNI_info!
268 :
269 0 : JLocalAutoRef jo_exc( *this, m_env->ExceptionOccurred() );
270 0 : m_env->ExceptionClear();
271 : assert(jo_exc.is());
272 0 : if (! jo_exc.is())
273 : {
274 : throw BridgeRuntimeError(
275 0 : "java exception occurred, but not available!?" +
276 0 : get_stack_trace() );
277 : }
278 :
279 : // call toString(); don't rely on m_jni_info
280 0 : jclass jo_class = m_env->FindClass( "java/lang/Object" );
281 0 : if (JNI_FALSE != m_env->ExceptionCheck())
282 : {
283 0 : m_env->ExceptionClear();
284 : throw BridgeRuntimeError(
285 0 : "cannot get class java.lang.Object!" + get_stack_trace() );
286 : }
287 0 : JLocalAutoRef jo_Object( *this, jo_class );
288 : // method Object.toString()
289 : jmethodID method_Object_toString = m_env->GetMethodID(
290 0 : (jclass) jo_Object.get(), "toString", "()Ljava/lang/String;" );
291 0 : if (JNI_FALSE != m_env->ExceptionCheck())
292 : {
293 0 : m_env->ExceptionClear();
294 : throw BridgeRuntimeError(
295 0 : "cannot get method id of java.lang.Object.toString()!" +
296 0 : get_stack_trace() );
297 : }
298 : assert(method_Object_toString != 0);
299 :
300 : JLocalAutoRef jo_descr(
301 : *this, m_env->CallObjectMethodA(
302 0 : jo_exc.get(), method_Object_toString, 0 ) );
303 0 : if (m_env->ExceptionCheck()) // no chance at all
304 : {
305 0 : m_env->ExceptionClear();
306 : throw BridgeRuntimeError(
307 0 : "error examining java exception object!" +
308 0 : get_stack_trace() );
309 : }
310 :
311 0 : jsize len = m_env->GetStringLength( (jstring) jo_descr.get() );
312 : std::unique_ptr< rtl_mem > ustr_mem(
313 : rtl_mem::allocate(
314 0 : sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
315 0 : rtl_uString * ustr = (rtl_uString *)ustr_mem.get();
316 0 : m_env->GetStringRegion( (jstring) jo_descr.get(), 0, len, ustr->buffer );
317 0 : if (m_env->ExceptionCheck())
318 : {
319 0 : m_env->ExceptionClear();
320 : throw BridgeRuntimeError(
321 0 : "invalid java string object!" + get_stack_trace() );
322 : }
323 0 : ustr->refCount = 1;
324 0 : ustr->length = len;
325 0 : ustr->buffer[ len ] = '\0';
326 0 : OUString message( (rtl_uString *)ustr_mem.release(), SAL_NO_ACQUIRE );
327 :
328 0 : throw BridgeRuntimeError( message + get_stack_trace( jo_exc.get() ) );
329 : }
330 :
331 :
332 6 : void JNI_context::getClassForName(
333 : jclass * classClass, jmethodID * methodForName) const
334 : {
335 6 : jclass c = m_env->FindClass("java/lang/Class");
336 6 : if (c != 0) {
337 : *methodForName = m_env->GetStaticMethodID(
338 : c, "forName",
339 6 : "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
340 : }
341 6 : *classClass = c;
342 6 : }
343 :
344 :
345 208 : jclass JNI_context::findClass(
346 : char const * name, jclass classClass, jmethodID methodForName,
347 : bool inException) const
348 : {
349 208 : jclass c = 0;
350 208 : JLocalAutoRef s(*this, m_env->NewStringUTF(name));
351 208 : if (s.is()) {
352 : jvalue a[3];
353 208 : a[0].l = s.get();
354 208 : a[1].z = JNI_FALSE;
355 208 : a[2].l = m_class_loader;
356 : c = static_cast< jclass >(
357 208 : m_env->CallStaticObjectMethodA(classClass, methodForName, a));
358 : }
359 208 : if (!inException) {
360 208 : ensure_no_exception();
361 : }
362 208 : return c;
363 : }
364 :
365 :
366 0 : OUString JNI_context::get_stack_trace( jobject jo_exc ) const
367 : {
368 : JLocalAutoRef jo_JNI_proxy(
369 : *this,
370 0 : find_class( *this, "com.sun.star.bridges.jni_uno.JNI_proxy", true ) );
371 0 : if (assert_no_exception())
372 : {
373 : // static method JNI_proxy.get_stack_trace()
374 : jmethodID method = m_env->GetStaticMethodID(
375 0 : (jclass) jo_JNI_proxy.get(), "get_stack_trace",
376 0 : "(Ljava/lang/Throwable;)Ljava/lang/String;" );
377 0 : if (assert_no_exception() && (0 != method))
378 : {
379 : jvalue arg;
380 0 : arg.l = jo_exc;
381 : JLocalAutoRef jo_stack_trace(
382 : *this, m_env->CallStaticObjectMethodA(
383 0 : (jclass) jo_JNI_proxy.get(), method, &arg ) );
384 0 : if (assert_no_exception())
385 : {
386 : jsize len =
387 0 : m_env->GetStringLength( (jstring) jo_stack_trace.get() );
388 : std::unique_ptr< rtl_mem > ustr_mem(
389 : rtl_mem::allocate(
390 0 : sizeof (rtl_uString) + (len * sizeof (sal_Unicode)) ) );
391 0 : rtl_uString * ustr = (rtl_uString *)ustr_mem.get();
392 : m_env->GetStringRegion(
393 0 : (jstring) jo_stack_trace.get(), 0, len, ustr->buffer );
394 0 : if (assert_no_exception())
395 : {
396 0 : ustr->refCount = 1;
397 0 : ustr->length = len;
398 0 : ustr->buffer[ len ] = '\0';
399 : return OUString(
400 0 : (rtl_uString *)ustr_mem.release(), SAL_NO_ACQUIRE );
401 0 : }
402 0 : }
403 : }
404 : }
405 0 : return OUString();
406 : }
407 :
408 : }
409 :
410 : using namespace ::jni_uno;
411 :
412 : extern "C"
413 : {
414 : namespace
415 : {
416 :
417 :
418 0 : void SAL_CALL java_env_disposing( uno_Environment * java_env )
419 : SAL_THROW_EXTERN_C()
420 : {
421 : ::jvmaccess::UnoVirtualMachine * machine =
422 : reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
423 0 : java_env->pContext );
424 0 : java_env->pContext = 0;
425 0 : machine->release();
426 0 : }
427 : }
428 :
429 : #ifdef DISABLE_DYNLOADING
430 : #define uno_initEnvironment java_uno_initEnvironment
431 : #endif
432 :
433 :
434 6 : SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_initEnvironment( uno_Environment * java_env )
435 : SAL_THROW_EXTERN_C()
436 : {
437 6 : java_env->environmentDisposing = java_env_disposing;
438 6 : java_env->pExtEnv = 0; // no extended support
439 : assert(java_env->pContext != 0);
440 :
441 : ::jvmaccess::UnoVirtualMachine * machine =
442 : reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
443 6 : java_env->pContext );
444 6 : machine->acquire();
445 6 : }
446 :
447 : #ifdef DISABLE_DYNLOADING
448 : #define uno_ext_getMapping java_uno_ext_getMapping
449 : #endif
450 :
451 :
452 6 : SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_ext_getMapping(
453 : uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo )
454 : SAL_THROW_EXTERN_C()
455 : {
456 : assert(ppMapping != 0);
457 : assert(pFrom != 0);
458 : assert(pTo != 0);
459 6 : if (0 != *ppMapping)
460 : {
461 0 : (*(*ppMapping)->release)( *ppMapping );
462 0 : *ppMapping = 0;
463 : }
464 :
465 : BOOST_STATIC_ASSERT( JNI_FALSE == sal_False );
466 : BOOST_STATIC_ASSERT( JNI_TRUE == sal_True );
467 : BOOST_STATIC_ASSERT( sizeof (jboolean) == sizeof (sal_Bool) );
468 : BOOST_STATIC_ASSERT( sizeof (jchar) == sizeof (sal_Unicode) );
469 : BOOST_STATIC_ASSERT( sizeof (jdouble) == sizeof (double) );
470 : BOOST_STATIC_ASSERT( sizeof (jfloat) == sizeof (float) );
471 : BOOST_STATIC_ASSERT( sizeof (jbyte) == sizeof (sal_Int8) );
472 : BOOST_STATIC_ASSERT( sizeof (jshort) == sizeof (sal_Int16) );
473 : BOOST_STATIC_ASSERT( sizeof (jint) == sizeof (sal_Int32) );
474 : BOOST_STATIC_ASSERT( sizeof (jlong) == sizeof (sal_Int64) );
475 :
476 : OUString const & from_env_typename =
477 6 : OUString::unacquired( &pFrom->pTypeName );
478 : OUString const & to_env_typename =
479 6 : OUString::unacquired( &pTo->pTypeName );
480 :
481 6 : uno_Mapping * mapping = 0;
482 :
483 : try
484 : {
485 6 : if ( from_env_typename == UNO_LB_JAVA && to_env_typename == UNO_LB_UNO )
486 : {
487 : Bridge * bridge =
488 6 : new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1
489 6 : mapping = &bridge->m_java2uno;
490 : uno_registerMapping(
491 : &mapping, Bridge_free,
492 6 : pFrom, (uno_Environment *)pTo->pExtEnv, 0 );
493 : }
494 0 : else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_JAVA )
495 : {
496 : Bridge * bridge =
497 0 : new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1
498 0 : mapping = &bridge->m_uno2java;
499 : uno_registerMapping(
500 : &mapping, Bridge_free,
501 0 : (uno_Environment *)pFrom->pExtEnv, pTo, 0 );
502 : }
503 : }
504 0 : catch (const BridgeRuntimeError & err)
505 : {
506 : SAL_WARN(
507 : "bridges",
508 : "ingoring BridgeRuntimeError \"" << err.m_message << "\"");
509 : }
510 0 : catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
511 : {
512 : SAL_WARN("bridges", "attaching current thread to java failed");
513 : }
514 :
515 6 : *ppMapping = mapping;
516 6 : }
517 :
518 : }
519 :
520 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|