1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
19 #include "pyuno_impl.hxx"
21 #include <o3tl/any.hxx>
23 #include <com/sun/star/beans/MethodConcept.hpp>
24 #include <com/sun/star/beans/UnknownPropertyException.hpp>
25 #include <com/sun/star/script/CannotConvertException.hpp>
26 #include <com/sun/star/script/XInvocationAdapterFactory2.hpp>
27 #include <com/sun/star/beans/XIntrospection.hpp>
29 #include <comphelper/sequence.hxx>
30 #include <cppuhelper/exc_hlp.hxx>
31 #include <cppuhelper/typeprovider.hxx>
34 using com::sun::star::beans::XIntrospectionAccess
;
35 using com::sun::star::uno::Any
;
36 using com::sun::star::uno::makeAny
;
37 using com::sun::star::uno::Reference
;
38 using com::sun::star::uno::Sequence
;
39 using com::sun::star::uno::RuntimeException
;
40 using com::sun::star::uno::XInterface
;
41 using com::sun::star::uno::Type
;
42 using com::sun::star::lang::XUnoTunnel
;
43 using com::sun::star::lang::IllegalArgumentException
;
44 using com::sun::star::beans::UnknownPropertyException
;
45 using com::sun::star::script::CannotConvertException
;
46 using com::sun::star::reflection::InvocationTargetException
;
47 using com::sun::star::reflection::XIdlMethod
;
48 using com::sun::star::reflection::ParamInfo
;
50 #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
55 Adapter::Adapter( const PyRef
& ref
, const Sequence
< Type
> &types
)
56 : mWrappedObject( ref
),
57 mInterpreter( (PyThreadState_Get()->interp
) ),
63 // Problem: We don't know, if we have the python interpreter lock
64 // There is no runtime function to get to know this.
65 decreaseRefCount( mInterpreter
, mWrappedObject
.get() );
66 mWrappedObject
.scratch();
69 static cppu::OImplementationId
g_id( false );
71 Sequence
<sal_Int8
> Adapter::getUnoTunnelId()
73 return g_id
.getImplementationId();
76 sal_Int64
Adapter::getSomething( const Sequence
< sal_Int8
> &id
)
78 if( id
== g_id
.getImplementationId() )
79 return reinterpret_cast<sal_Int64
>(this);
83 void raiseInvocationTargetExceptionWhenNeeded( const Runtime
&runtime
)
85 if( !Py_IsInitialized() )
86 throw InvocationTargetException();
88 if( PyErr_Occurred() )
90 PyRef excType
, excValue
, excTraceback
;
91 PyErr_Fetch(reinterpret_cast<PyObject
**>(&excType
), reinterpret_cast<PyObject
**>(&excValue
), reinterpret_cast<PyObject
**>(&excTraceback
));
92 Any
unoExc( runtime
.extractUnoException( excType
, excValue
, excTraceback
) );
93 throw InvocationTargetException(
94 o3tl::doAccess
<css::uno::Exception
>(unoExc
)->Message
,
95 Reference
<XInterface
>(), unoExc
);
99 Reference
< XIntrospectionAccess
> Adapter::getIntrospection()
102 return Reference
< XIntrospectionAccess
> ();
105 Sequence
< sal_Int16
> Adapter::getOutIndexes( const OUString
& functionName
)
107 Sequence
< sal_Int16
> ret
;
108 MethodOutIndexMap::const_iterator ii
= m_methodOutIndexMap
.find( functionName
);
109 if( ii
== m_methodOutIndexMap
.end() )
114 PyThreadDetach antiguard
;
116 // retrieve the adapter object again. It will be the same instance as before,
117 // (the adapter factory keeps a weak map inside, which I couldn't have outside)
118 Reference
< XInterface
> unoAdapterObject
=
119 runtime
.getImpl()->cargo
->xAdapterFactory
->createAdapter( this, mTypes
);
121 // uuuh, that's really expensive. The alternative would have been, to store
122 // an instance of the introspection at (this), but this results in a cyclic
123 // reference, which is never broken (as it is up to OOo1.1.0).
124 Reference
< XIntrospectionAccess
> introspection
=
125 runtime
.getImpl()->cargo
->xIntrospection
->inspect( makeAny( unoAdapterObject
) );
127 if( !introspection
.is() )
129 throw RuntimeException(
130 "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" );
133 Reference
< XIdlMethod
> method
= introspection
->getMethod(
134 functionName
, css::beans::MethodConcept::ALL
);
137 throw RuntimeException(
138 "pyuno bridge: Couldn't get reflection for method " + functionName
);
141 const Sequence
< ParamInfo
> seqInfo
= method
->getParameterInfos();
142 std::vector
<sal_Int16
> retVec
;
143 for( sal_Int32 i
= 0; i
< seqInfo
.getLength(); ++i
)
145 if( seqInfo
[i
].aMode
== css::reflection::ParamMode_OUT
||
146 seqInfo
[i
].aMode
== css::reflection::ParamMode_INOUT
)
148 retVec
.push_back(static_cast<sal_Int16
>(i
));
152 ret
= comphelper::containerToSequence(retVec
);
154 // guard active again !
155 m_methodOutIndexMap
[ functionName
] = ret
;
164 Any
Adapter::invoke( const OUString
&aFunctionName
,
165 const Sequence
< Any
>& aParams
,
166 Sequence
< sal_Int16
> &aOutParamIndex
,
167 Sequence
< Any
> &aOutParam
)
171 // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
172 // always handled by the adapter directly.
173 if( aParams
.getLength() == 1 && aFunctionName
== "getSomething" )
175 Sequence
< sal_Int8
> id
;
176 if( aParams
[0] >>= id
)
177 return css::uno::makeAny( getSomething( id
) );
181 RuntimeCargo
*cargo
= nullptr;
184 PyThreadAttach
guard( mInterpreter
);
186 if( !Py_IsInitialized() )
187 throw InvocationTargetException();
189 // convert parameters to python args
190 // TODO: Out parameter
192 cargo
= runtime
.getImpl()->cargo
;
193 if( isLog( cargo
, LogLevel::CALL
) )
195 logCall( cargo
, "try uno->py[0x",
196 mWrappedObject
.get(), aFunctionName
, aParams
);
199 sal_Int32 size
= aParams
.getLength();
200 PyRef
argsTuple(PyTuple_New( size
), SAL_NO_ACQUIRE
, NOT_NULL
);
202 // fill tuple with default values in case of exceptions
203 for( i
= 0 ;i
< size
; i
++ )
205 Py_INCREF( Py_None
);
206 PyTuple_SetItem( argsTuple
.get(), i
, Py_None
);
209 // convert args to python
210 for( i
= 0; i
< size
; i
++ )
212 PyRef val
= runtime
.any2PyObject( aParams
[i
] );
214 // any2PyObject() can release the GIL
215 if( !Py_IsInitialized() )
216 throw InvocationTargetException();
218 PyTuple_SetItem( argsTuple
.get(), i
, val
.getAcquired() );
222 PyRef
method(PyObject_GetAttrString( mWrappedObject
.get(), TO_ASCII(aFunctionName
)),
225 raiseInvocationTargetExceptionWhenNeeded( runtime
);
228 PyRef
str( PyObject_Repr( mWrappedObject
.get() ), SAL_NO_ACQUIRE
);
230 OUString sMsg
= "pyuno::Adapter: Method "
232 + " is not implemented at object "
233 + pyString2ustring(str
.get());
234 throw IllegalArgumentException( sMsg
, Reference
< XInterface
> (),0 );
237 PyRef
pyRet( PyObject_CallObject( method
.get(), argsTuple
.get() ), SAL_NO_ACQUIRE
);
238 raiseInvocationTargetExceptionWhenNeeded( runtime
);
241 ret
= runtime
.pyObject2Any( pyRet
);
243 if( ret
.hasValue() &&
244 ret
.getValueTypeClass() == css::uno::TypeClass_SEQUENCE
&&
245 aFunctionName
!= "getTypes" && // needed by introspection itself !
246 aFunctionName
!= "getImplementationId" ) // needed by introspection itself !
248 // the sequence can either be
249 // 1) a simple sequence return value
250 // 2) a sequence, where the first element is the return value
251 // and the following elements are interpreted as the outparameter
252 // I can only decide for one solution by checking the method signature,
253 // so I need the reflection of the adapter !
254 aOutParamIndex
= getOutIndexes( aFunctionName
);
255 if( aOutParamIndex
.hasElements() )
257 // out parameters exist, extract the sequence
259 if( ! ( ret
>>= seq
) )
261 throw RuntimeException(
262 "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName
);
265 auto nOutLength
= aOutParamIndex
.getLength();
266 if( nOutLength
+ 1 != seq
.getLength() )
268 OUString sMsg
= "pyuno bridge: expected for method "
270 + " one return value and "
271 + OUString::number(nOutLength
)
272 + " out parameters, got a sequence of "
273 + OUString::number(seq
.getLength())
274 + " elements as return value.";
275 throw RuntimeException( sMsg
, *this );
278 aOutParam
.realloc( nOutLength
);
280 std::copy_n(std::next(seq
.begin()), nOutLength
, aOutParam
.begin());
282 // else { sequence is a return value !}
286 // log the reply, if desired
287 if( isLog( cargo
, LogLevel::CALL
) )
289 logReply( cargo
, "success uno->py[0x" ,
290 mWrappedObject
.get(), aFunctionName
, ret
, aOutParam
);
295 catch( const InvocationTargetException
& e
)
297 if( isLog( cargo
, LogLevel::CALL
) )
300 cargo
, "except uno->py[0x" ,
301 mWrappedObject
.get(), aFunctionName
,
302 e
.TargetException
.getValue(),e
.TargetException
.getValueType() );
306 catch( const IllegalArgumentException
& e
)
308 if( isLog( cargo
, LogLevel::CALL
) )
311 cargo
, "except uno->py[0x" ,
312 mWrappedObject
.get(), aFunctionName
, &e
,cppu::UnoType
<decltype(e
)>::get() );
316 catch( const RuntimeException
& e
)
318 if( cargo
&& isLog( cargo
, LogLevel::CALL
) )
321 cargo
, "except uno->py[0x" ,
322 mWrappedObject
.get(), aFunctionName
, &e
,cppu::UnoType
<decltype(e
)>::get() );
326 catch( const CannotConvertException
& e
)
328 if( isLog( cargo
, LogLevel::CALL
) )
331 cargo
, "except uno->py[0x" ,
332 mWrappedObject
.get(), aFunctionName
, &e
,cppu::UnoType
<decltype(e
)>::get() );
339 void Adapter::setValue( const OUString
& aPropertyName
, const Any
& value
)
341 if( !hasProperty( aPropertyName
) )
343 throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName
+ " is unknown." );
346 PyThreadAttach
guard( mInterpreter
);
349 if( !Py_IsInitialized() )
350 throw InvocationTargetException();
353 PyRef obj
= runtime
.any2PyObject( value
);
355 // any2PyObject() can release the GIL
356 if( !Py_IsInitialized() )
357 throw InvocationTargetException();
359 PyObject_SetAttrString(
360 mWrappedObject
.get(), TO_ASCII(aPropertyName
), obj
.get() );
361 raiseInvocationTargetExceptionWhenNeeded( runtime
);
364 catch( const IllegalArgumentException
& exc
)
366 css::uno::Any anyEx
= cppu::getCaughtException();
367 throw InvocationTargetException( exc
.Message
, *this, anyEx
);
371 Any
Adapter::getValue( const OUString
& aPropertyName
)
374 PyThreadAttach
guard( mInterpreter
);
376 // Should probably be InvocationTargetException, but the interface doesn't allow it
377 if( !Py_IsInitialized() )
378 throw RuntimeException();
382 PyObject_GetAttrString( mWrappedObject
.get(), TO_ASCII(aPropertyName
) ),
385 if (!pyRef
.is() || PyErr_Occurred())
387 throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName
+ " is unknown." );
389 ret
= runtime
.pyObject2Any( pyRef
);
394 sal_Bool
Adapter::hasMethod( const OUString
& aMethodName
)
396 return hasProperty( aMethodName
);
399 sal_Bool
Adapter::hasProperty( const OUString
& aPropertyName
)
402 PyThreadAttach
guard( mInterpreter
);
404 // Should probably be InvocationTargetException, but the interface doesn't allow it
405 if( !Py_IsInitialized() )
406 throw RuntimeException();
408 bRet
= PyObject_HasAttrString(
409 mWrappedObject
.get() , TO_ASCII( aPropertyName
));
416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */