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