/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include "jni_bridge.h" #include "jniunoenvironmentdata.hxx" namespace jni_uno { jobject Bridge::map_to_java( JNI_context const & jni, uno_Interface * pUnoI, JNI_interface_type_info const * info ) const { // get oid rtl_uString * pOid = nullptr; (*m_uno_env->getObjectIdentifier)( m_uno_env, &pOid, pUnoI ); assert( pOid != nullptr ); OUString oid( pOid, SAL_NO_ACQUIRE ); // opt getRegisteredInterface() JLocalAutoRef jo_oid( jni, ustring_to_jstring( jni, oid.pData ) ); jvalue args[ 2 ]; args[ 0 ].l = jo_oid.get(); args[ 1 ].l = info->m_type; jobject jo_iface = jni->CallObjectMethodA( getJniInfo()->m_object_java_env, getJniInfo()->m_method_IEnvironment_getRegisteredInterface, args ); jni.ensure_no_exception(); if (jo_iface == nullptr) // no registered iface { // register uno interface (*m_uno_env->registerInterface)( m_uno_env, reinterpret_cast< void ** >( &pUnoI ), oid.pData, reinterpret_cast(info->m_td.get()) ); // create java and register java proxy jvalue args2[ 8 ]; acquire(); args2[ 0 ].j = reinterpret_cast< sal_Int64 >( this ); (*pUnoI->acquire)( pUnoI ); args2[ 1 ].l = getJniInfo()->m_object_java_env; args2[ 2 ].j = reinterpret_cast< sal_Int64 >( pUnoI ); typelib_typedescription_acquire( info->m_td.get() ); args2[ 3 ].j = reinterpret_cast< sal_Int64 >( info->m_td.get() ); args2[ 4 ].l = info->m_type; args2[ 5 ].l = jo_oid.get(); args2[ 6 ].l = info->m_proxy_ctor; auto * envData = static_cast( m_java_env->pContext); { osl::MutexGuard g(envData->mutex); args2[ 7 ].l = envData->asynchronousFinalizer; } jo_iface = jni->CallStaticObjectMethodA( getJniInfo()->m_class_JNI_proxy, getJniInfo()->m_method_JNI_proxy_create, args2 ); jni.ensure_no_exception(); } assert( jo_iface != nullptr ); return jo_iface; } void Bridge::handle_uno_exc( JNI_context const & jni, uno_Any * uno_exc ) const { if (uno_exc->pType->eTypeClass == typelib_TypeClass_EXCEPTION) { #if OSL_DEBUG_LEVEL > 0 // append java stack trace to Message member static_cast< ::com::sun::star::uno::Exception * >( uno_exc->pData )->Message += jni.get_stack_trace(); #endif SAL_INFO( "bridges", "exception occurred java->uno: [" << OUString::unacquired(&uno_exc->pType->pTypeName) << "] " << static_cast( uno_exc->pData)->Message); // signal exception jvalue java_exc; try { map_to_java( jni, &java_exc, uno_exc->pData, uno_exc->pType, nullptr, true /* in */, false /* no out */ ); } catch (...) { uno_any_destruct( uno_exc, nullptr ); throw; } uno_any_destruct( uno_exc, nullptr ); JLocalAutoRef jo_exc( jni, java_exc.l ); jint res = jni->Throw( static_cast(jo_exc.get()) ); if (res != 0) { // call toString() JLocalAutoRef jo_descr( jni, jni->CallObjectMethodA( jo_exc.get(), getJniInfo()->m_method_Object_toString, nullptr ) ); jni.ensure_no_exception(); throw BridgeRuntimeError( "throwing java exception failed: " + jstring_to_oustring( jni, static_cast(jo_descr.get()) ) + jni.get_stack_trace() ); } } else { OUString message( "thrown exception is no uno exception: " + OUString::unacquired( &uno_exc->pType->pTypeName ) + jni.get_stack_trace() ); uno_any_destruct( uno_exc, nullptr ); throw BridgeRuntimeError( message ); } } union largest { sal_Int64 n; double d; void * p; uno_Any a; }; jobject Bridge::call_uno( JNI_context const & jni, uno_Interface * pUnoI, typelib_TypeDescription * member_td, typelib_TypeDescriptionReference * return_type, sal_Int32 nParams, typelib_MethodParameter const * pParams, jobjectArray jo_args /* may be 0 */ ) const { // return mem sal_Int32 return_size; switch (return_type->eTypeClass) { case typelib_TypeClass_VOID: return_size = 0; break; case typelib_TypeClass_STRUCT: case typelib_TypeClass_EXCEPTION: return_size = std::max( TypeDescr(return_type).get()->nSize, static_cast< sal_Int32 >(sizeof (largest))); break; default: return_size = sizeof (largest); break; } char * mem = static_cast(alloca( (nParams * sizeof (void *)) + return_size + (nParams * sizeof (largest)) )); void ** uno_args = reinterpret_cast(mem); void * uno_ret = return_size == 0 ? nullptr : (mem + (nParams * sizeof (void *))); largest * uno_args_mem = reinterpret_cast (mem + (nParams * sizeof (void *)) + return_size); assert( (nParams == 0) || (nParams == jni->GetArrayLength( jo_args )) ); for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) { typelib_MethodParameter const & param = pParams[ nPos ]; typelib_TypeDescriptionReference * type = param.pTypeRef; uno_args[ nPos ] = &uno_args_mem[ nPos ]; if (type->eTypeClass == typelib_TypeClass_STRUCT || type->eTypeClass == typelib_TypeClass_EXCEPTION) { TypeDescr td( type ); if (sal::static_int_cast< sal_uInt32 >(td.get()->nSize) > sizeof (largest)) uno_args[ nPos ] = alloca( td.get()->nSize ); } if (param.bIn) { try { JLocalAutoRef jo_arg( jni, jni->GetObjectArrayElement( jo_args, nPos ) ); jni.ensure_no_exception(); jvalue java_arg; java_arg.l = jo_arg.get(); map_to_uno( jni, uno_args[ nPos ], java_arg, type, nullptr, false /* no assign */, param.bOut, true /* special wrapped integral types */ ); } catch (...) { // cleanup uno in args for ( sal_Int32 n = 0; n < nPos; ++n ) { typelib_MethodParameter const & p = pParams[ n ]; if (p.bIn) { uno_type_destructData( uno_args[ n ], p.pTypeRef, nullptr ); } } throw; } } } uno_Any uno_exc_holder; uno_Any * uno_exc = &uno_exc_holder; // call binary uno (*pUnoI->pDispatcher)( pUnoI, member_td, uno_ret, uno_args, &uno_exc ); if (uno_exc == nullptr) { // convert out args; destruct uno args for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) { typelib_MethodParameter const & param = pParams[ nPos ]; typelib_TypeDescriptionReference * type = param.pTypeRef; if (param.bOut) { try { // get out holder array[ 1 ] JLocalAutoRef jo_out_holder( jni, jni->GetObjectArrayElement( jo_args, nPos ) ); jni.ensure_no_exception(); jvalue java_arg; java_arg.l = jo_out_holder.get(); map_to_java( jni, &java_arg, uno_args[ nPos ], type, nullptr, true /* in */, true /* out holder */ ); } catch (...) { // cleanup further uno args for ( sal_Int32 n = nPos; n < nParams; ++n ) { uno_type_destructData( uno_args[ n ], pParams[ n ].pTypeRef, nullptr ); } // cleanup uno return value uno_type_destructData( uno_ret, return_type, nullptr ); throw; } } if (typelib_TypeClass_DOUBLE < type->eTypeClass && type->eTypeClass != typelib_TypeClass_ENUM) // opt { uno_type_destructData( uno_args[ nPos ], type, nullptr ); } } if (return_type->eTypeClass != typelib_TypeClass_VOID) { // convert uno return value jvalue java_ret; try { map_to_java( jni, &java_ret, uno_ret, return_type, nullptr, true /* in */, false /* no out */, true /* special_wrapped_integral_types */ ); } catch (...) { uno_type_destructData( uno_ret, return_type, nullptr ); throw; } if (typelib_TypeClass_DOUBLE < return_type->eTypeClass && return_type->eTypeClass != typelib_TypeClass_ENUM) // opt { uno_type_destructData( uno_ret, return_type, nullptr ); } return java_ret.l; } return nullptr; // void return } else // exception occurred { // destruct uno in args for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) { typelib_MethodParameter const & param = pParams[ nPos ]; if (param.bIn) uno_type_destructData( uno_args[ nPos ], param.pTypeRef, nullptr ); } handle_uno_exc( jni, uno_exc ); return nullptr; } } } using namespace ::jni_uno; extern "C" { SAL_JNI_EXPORT jobject JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_dispatch_1call( JNIEnv * jni_env, jobject jo_proxy, jlong bridge_handle, jstring jo_method, jobjectArray jo_args /* may be 0 */ ) { Bridge const * bridge = reinterpret_cast< Bridge const * >( bridge_handle ); JNI_info const * jni_info = bridge->getJniInfo(); JNI_context jni( jni_info, jni_env, static_cast< jobject >( static_cast(bridge->m_java_env->pContext) ->machine->getClassLoader())); OUString method_name; try { method_name = jstring_to_oustring( jni, jo_method ); SAL_INFO( "bridges", "java->uno call: " << method_name << " on oid " << jstring_to_oustring( jni, static_cast( JLocalAutoRef( jni, jni->GetObjectField( jo_proxy, jni_info->m_field_JNI_proxy_m_oid)) .get()))); // special IQueryInterface.queryInterface() if ( method_name == "queryInterface" ) { // oid JLocalAutoRef jo_oid( jni, jni->GetObjectField( jo_proxy, jni_info->m_field_JNI_proxy_m_oid ) ); // type JLocalAutoRef jo_type( jni, jni->GetObjectArrayElement( jo_args, 0 ) ); jni.ensure_no_exception(); JLocalAutoRef jo_type_name( jni, jni->GetObjectField( jo_type.get(), jni_info->m_field_Type_typeName ) ); if (! jo_type_name.is()) { throw BridgeRuntimeError( "incomplete type object: no type name!" + jni.get_stack_trace() ); } OUString type_name( jstring_to_oustring( jni, static_cast(jo_type_name.get()) ) ); JNI_type_info const * info = jni_info->get_type_info( jni, type_name ); if (info->m_td.get()->eTypeClass != typelib_TypeClass_INTERFACE) { throw BridgeRuntimeError( "queryInterface() call demands an INTERFACE type!" ); } JNI_interface_type_info const * iface_info = static_cast< JNI_interface_type_info const * >( info ); // getRegisteredInterface() already tested in JNI_proxy: // perform queryInterface call on binary uno interface uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >( jni->GetLongField( jo_proxy, jni_info->m_field_JNI_proxy_m_receiver_handle ) ); uno_Any uno_ret; void * uno_args[] = { &iface_info->m_td.get()->pWeakRef }; uno_Any uno_exc_holder; uno_Any * uno_exc = &uno_exc_holder; // call binary uno (*pUnoI->pDispatcher)( pUnoI, jni_info->m_XInterface_queryInterface_td.get(), &uno_ret, uno_args, &uno_exc ); if (uno_exc == nullptr) { jobject jo_ret = nullptr; if (uno_ret.pType->eTypeClass == typelib_TypeClass_INTERFACE) { uno_Interface * pUnoRet = static_cast(uno_ret.pReserved); if (pUnoRet != nullptr) { try { jo_ret = bridge->map_to_java( jni, pUnoRet, iface_info ); } catch (...) { uno_any_destruct( &uno_ret, nullptr ); throw; } } } uno_any_destruct( &uno_ret, nullptr ); return jo_ret; } else { bridge->handle_uno_exc( jni, uno_exc ); return nullptr; } } typelib_InterfaceTypeDescription * td = reinterpret_cast< typelib_InterfaceTypeDescription * >( jni->GetLongField( jo_proxy, jni_info->m_field_JNI_proxy_m_td_handle ) ); uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >( jni->GetLongField( jo_proxy, jni_info->m_field_JNI_proxy_m_receiver_handle ) ); typelib_TypeDescriptionReference ** ppAllMembers = td->ppAllMembers; for ( sal_Int32 nPos = td->nAllMembers; nPos--; ) { // try to avoid getting typedescription as long as possible, // because of a Mutex.acquire() in // typelib_typedescriptionreference_getDescription() typelib_TypeDescriptionReference * member_type = ppAllMembers[ nPos ]; // check method_name against fully qualified type_name // of member_type; type_name is of the form // "::" *(":@" "," ":" ) OUString const & type_name = OUString::unacquired( &member_type->pTypeName ); sal_Int32 offset = type_name.indexOf( ':' ) + 2; assert(offset >= 2); assert(offset < type_name.getLength()); assert(type_name[offset - 1] == ':' ); sal_Int32 remainder = type_name.getLength() - offset; if (member_type->eTypeClass == typelib_TypeClass_INTERFACE_METHOD) { if ((method_name.getLength() == remainder || (method_name.getLength() < remainder && type_name[offset + method_name.getLength()] == ':')) && type_name.match(method_name, offset)) { TypeDescr member_td( member_type ); typelib_InterfaceMethodTypeDescription * method_td = reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( member_td.get() ); return bridge->call_uno( jni, pUnoI, member_td.get(), method_td->pReturnTypeRef, method_td->nParams, method_td->pParams, jo_args ); } } else // attribute { assert( member_type->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE ); if (method_name.getLength() >= 3 && (method_name.getLength() - 3 == remainder || (method_name.getLength() - 3 < remainder && type_name[ offset + (method_name.getLength() - 3)] == ':')) && method_name[1] == 'e' && method_name[2] == 't' && rtl_ustr_compare_WithLength( type_name.getStr() + offset, method_name.getLength() - 3, method_name.getStr() + 3, method_name.getLength() - 3) == 0) { if (method_name[ 0 ] == 'g') { TypeDescr member_td( member_type ); typelib_InterfaceAttributeTypeDescription * attr_td = reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( member_td.get() ); return bridge->call_uno( jni, pUnoI, member_td.get(), attr_td->pAttributeTypeRef, 0, nullptr, jo_args ); } else if (method_name[ 0 ] == 's') { TypeDescr member_td( member_type ); typelib_InterfaceAttributeTypeDescription * attr_td = reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( member_td.get() ); if (! attr_td->bReadOnly) { typelib_MethodParameter param; param.pTypeRef = attr_td->pAttributeTypeRef; param.bIn = true; param.bOut = false; return bridge->call_uno( jni, pUnoI, member_td.get(), jni_info->m_void_type.getTypeLibType(), 1, ¶m, jo_args ); } } } } } // the thing that should not be... no method info found! throw BridgeRuntimeError( "calling undeclared function on interface " + OUString::unacquired(&td->aBase.pTypeName) + ": " + method_name + jni.get_stack_trace() ); } catch (const BridgeRuntimeError & err) { SAL_WARN( "bridges", "Java calling UNO method " << method_name << ": " << err.m_message); // notify RuntimeException OString cstr_msg( "[jni_uno bridge error] Java calling UNO method " + OUStringToOString(method_name, RTL_TEXTENCODING_JAVA_UTF8) + ": " + OUStringToOString(err.m_message, RTL_TEXTENCODING_JAVA_UTF8)); if (jni->ThrowNew(jni_info->m_class_RuntimeException, cstr_msg.getStr()) != 0) { assert( false ); } return nullptr; } catch (const ::jvmaccess::VirtualMachine::AttachGuard::CreationException &) { SAL_WARN("bridges", "attaching current thread to java failed"); OString cstr_msg( "[jni_uno bridge error] attaching current thread to java failed" + OUStringToOString( jni.get_stack_trace(), RTL_TEXTENCODING_JAVA_UTF8)); if (jni->ThrowNew(jni_info->m_class_RuntimeException, cstr_msg.getStr()) != 0) { assert( false ); } return nullptr; } } SAL_JNI_EXPORT void JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_finalize__J( JNIEnv * jni_env, jobject jo_proxy, jlong bridge_handle ) { Bridge const * bridge = reinterpret_cast< Bridge const * >( bridge_handle ); JNI_info const * jni_info = bridge->getJniInfo(); JNI_context jni( jni_info, jni_env, static_cast< jobject >( static_cast(bridge->m_java_env->pContext) ->machine->getClassLoader())); uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >( jni->GetLongField( jo_proxy, jni_info->m_field_JNI_proxy_m_receiver_handle ) ); typelib_TypeDescription * td = reinterpret_cast< typelib_TypeDescription * >( jni->GetLongField( jo_proxy, jni_info->m_field_JNI_proxy_m_td_handle ) ); SAL_INFO( "bridges", "freeing java uno proxy: " << jstring_to_oustring( jni, static_cast( JLocalAutoRef( jni, jni->GetObjectField( jo_proxy, jni_info->m_field_JNI_proxy_m_oid)) .get()))); // revoke from uno env; has already been revoked from java env (*bridge->m_uno_env->revokeInterface)( bridge->m_uno_env, pUnoI ); // release receiver (*pUnoI->release)( pUnoI ); // release typedescription handle typelib_typedescription_release( td ); // release bridge handle bridge->release(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */