/* -*- 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 <sal/config.h>
#include <sal/log.hxx>
#include <algorithm>
#include <cassert>
#include "jni_bridge.h"
#include "jni_helper.h"
#include "jniunoenvironmentdata.hxx"
#include <com/sun/star/uno/Exception.hpp>
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<typelib_InterfaceTypeDescription *>(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<jni_uno::JniUnoEnvironmentData *>(
m_java_env->pContext);
{
std::unique_lock 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< css::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<css::uno::Exception const *>(
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<jthrowable>(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<jstring>(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 );
}
}
namespace {
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<char *>(alloca(
(nParams * sizeof (void *)) +
return_size + (nParams * sizeof (largest)) ));
void ** uno_args = reinterpret_cast<void **>(mem);
void * uno_ret = return_size == 0 ? nullptr : (mem + (nParams * sizeof (void *)));
largest * uno_args_mem = reinterpret_cast<largest *>
(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<JniUnoEnvironmentData *>(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<jstring>(
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<jstring>(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(
u"queryInterface() call demands an INTERFACE type!"_ustr );
}
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_Interface *>(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
// <name> "::" <method_name> *(":@" <idx> "," <idx> ":" <name>)
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<JniUnoEnvironmentData *>(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<jstring>(
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: */
↑ V505 The 'alloca' function is used inside the loop. This can quickly overflow stack.
↑ V1032 The pointer 'mem' is cast to a more strictly aligned pointer type.