LibreOffice Module pyuno (master) 1
pyuno_adapter.cxx
Go to the documentation of this file.
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#include "pyuno_impl.hxx"
20
21#include <o3tl/any.hxx>
22
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>
28
32#include <utility>
33
34
35using com::sun::star::beans::XIntrospectionAccess;
36using com::sun::star::uno::Any;
39using com::sun::star::uno::RuntimeException;
40using com::sun::star::uno::XInterface;
41using com::sun::star::uno::Type;
42using com::sun::star::lang::XUnoTunnel;
43using com::sun::star::lang::IllegalArgumentException;
44using com::sun::star::beans::UnknownPropertyException;
45using com::sun::star::script::CannotConvertException;
46using com::sun::star::reflection::InvocationTargetException;
47using com::sun::star::reflection::XIdlMethod;
48using com::sun::star::reflection::ParamInfo;
49
50#define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
51
52namespace pyuno
53{
54
56 : mWrappedObject(std::move( ref )),
57 mInterpreter( (PyThreadState_Get()->interp) ),
58 mTypes( types )
59{}
60
62{
63 // Problem: We don't know, if we have the python interpreter lock
64 // There is no runtime function to get to know this.
67}
68
70{
71 static const comphelper::UnoIdInit g_id;
72 return g_id.getSeq();
73}
74
76{
77 return comphelper::getSomethingImpl(id, this);
78}
79
81{
82 if( !Py_IsInitialized() )
83 throw InvocationTargetException();
84
85 if( PyErr_Occurred() )
86 {
87 PyRef excType, excValue, excTraceback;
88 PyErr_Fetch(reinterpret_cast<PyObject **>(&excType), reinterpret_cast<PyObject**>(&excValue), reinterpret_cast<PyObject**>(&excTraceback));
89 Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) );
90 throw InvocationTargetException(
91 o3tl::doAccess<css::uno::Exception>(unoExc)->Message,
92 Reference<XInterface>(), unoExc );
93 }
94}
95
97{
98 // not supported
100}
101
102Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName )
103{
105 MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName );
106 if( ii == m_methodOutIndexMap.end() )
107 {
108
109 Runtime runtime;
110 {
111 PyThreadDetach antiguard;
112
113 // retrieve the adapter object again. It will be the same instance as before,
114 // (the adapter factory keeps a weak map inside, which I couldn't have outside)
115 Reference< XInterface > unoAdapterObject =
116 runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes );
117
118 // uuuh, that's really expensive. The alternative would have been, to store
119 // an instance of the introspection at (this), but this results in a cyclic
120 // reference, which is never broken (as it is up to OOo1.1.0).
122 runtime.getImpl()->cargo->xIntrospection->inspect( Any( unoAdapterObject ) );
123
124 if( !introspection.is() )
125 {
126 throw RuntimeException(
127 "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" );
128 }
129
130 Reference< XIdlMethod > method = introspection->getMethod(
131 functionName, css::beans::MethodConcept::ALL );
132 if( ! method.is( ) )
133 {
134 throw RuntimeException(
135 "pyuno bridge: Couldn't get reflection for method " + functionName );
136 }
137
138 const Sequence< ParamInfo > seqInfo = method->getParameterInfos();
139 std::vector<sal_Int16> retVec;
140 for( sal_Int32 i = 0; i < seqInfo.getLength(); ++i )
141 {
142 if( seqInfo[i].aMode == css::reflection::ParamMode_OUT ||
143 seqInfo[i].aMode == css::reflection::ParamMode_INOUT )
144 {
145 retVec.push_back(static_cast<sal_Int16>(i));
146 }
147 }
148
150 }
151 // guard active again !
152 m_methodOutIndexMap[ functionName ] = ret;
153 }
154 else
155 {
156 ret = ii->second;
157 }
158 return ret;
159}
160
161Any Adapter::invoke( const OUString &aFunctionName,
162 const Sequence< Any >& aParams,
163 Sequence< sal_Int16 > &aOutParamIndex,
164 Sequence< Any > &aOutParam)
165{
166 Any ret;
167
168 // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
169 // always handled by the adapter directly.
170 if( aParams.getLength() == 1 && aFunctionName == "getSomething" )
171 {
173 if( aParams[0] >>= id )
174 return css::uno::Any( getSomething( id ) );
175
176 }
177
178 RuntimeCargo *cargo = nullptr;
179 try
180 {
182 {
183 if( !Py_IsInitialized() )
184 throw InvocationTargetException();
185
186 // convert parameters to python args
187 // TODO: Out parameter
188 Runtime runtime;
189 cargo = runtime.getImpl()->cargo;
190 if( isLog( cargo, LogLevel::CALL ) )
191 {
192 logCall( cargo, "try uno->py[0x",
193 mWrappedObject.get(), aFunctionName, aParams );
194 }
195
196 sal_Int32 size = aParams.getLength();
197 PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE, NOT_NULL );
198 int i;
199 // fill tuple with default values in case of exceptions
200 for( i = 0 ;i < size ; i ++ )
201 {
202 Py_INCREF( Py_None );
203 PyTuple_SetItem( argsTuple.get(), i, Py_None );
204 }
205
206 // convert args to python
207 for( i = 0; i < size ; i ++ )
208 {
209 PyRef val = runtime.any2PyObject( aParams[i] );
210
211 // any2PyObject() can release the GIL
212 if( !Py_IsInitialized() )
213 throw InvocationTargetException();
214
215 PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() );
216 }
217
218 // get callable
219 PyRef method(PyObject_GetAttrString( mWrappedObject.get(), TO_ASCII(aFunctionName)),
220 SAL_NO_ACQUIRE);
221
223 if( !method.is() )
224 {
225 PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
226
227 OUString sMsg = "pyuno::Adapter: Method "
228 + aFunctionName
229 + " is not implemented at object "
230 + pyString2ustring(str.get());
231 throw IllegalArgumentException( sMsg, Reference< XInterface > (),0 );
232 }
233
234 PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE );
236 if( pyRet.is() )
237 {
238 ret = runtime.pyObject2Any( pyRet );
239
240 if( ret.hasValue() &&
241 ret.getValueTypeClass() == css::uno::TypeClass_SEQUENCE &&
242 aFunctionName != "getTypes" && // needed by introspection itself !
243 aFunctionName != "getImplementationId" ) // needed by introspection itself !
244 {
245 // the sequence can either be
246 // 1) a simple sequence return value
247 // 2) a sequence, where the first element is the return value
248 // and the following elements are interpreted as the outparameter
249 // I can only decide for one solution by checking the method signature,
250 // so I need the reflection of the adapter !
251 aOutParamIndex = getOutIndexes( aFunctionName );
252 if( aOutParamIndex.hasElements() )
253 {
254 // out parameters exist, extract the sequence
255 Sequence< Any > seq;
256 if( ! ( ret >>= seq ) )
257 {
258 throw RuntimeException(
259 "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName );
260 }
261
262 auto nOutLength = aOutParamIndex.getLength();
263 if( nOutLength + 1 != seq.getLength() )
264 {
265 OUString sMsg = "pyuno bridge: expected for method "
266 + aFunctionName
267 + " one return value and "
268 + OUString::number(nOutLength)
269 + " out parameters, got a sequence of "
270 + OUString::number(seq.getLength())
271 + " elements as return value.";
272 throw RuntimeException( sMsg, *this );
273 }
274
275 aOutParam.realloc( nOutLength );
276 ret = std::as_const(seq)[0];
277 std::copy_n(std::next(std::cbegin(seq)), nOutLength, aOutParam.getArray());
278 }
279 // else { sequence is a return value !}
280 }
281 }
282
283 // log the reply, if desired
284 if( isLog( cargo, LogLevel::CALL ) )
285 {
286 logReply( cargo, "success uno->py[0x" ,
287 mWrappedObject.get(), aFunctionName, ret, aOutParam );
288 }
289 }
290
291 }
292 catch( const InvocationTargetException & e )
293 {
294 if( isLog( cargo, LogLevel::CALL ) )
295 {
297 cargo, "except uno->py[0x" ,
298 mWrappedObject.get(), aFunctionName,
299 e.TargetException.getValue(),e.TargetException.getValueType() );
300 }
301 throw;
302 }
303 catch( const IllegalArgumentException & e )
304 {
305 if( isLog( cargo, LogLevel::CALL ) )
306 {
308 cargo, "except uno->py[0x" ,
309 mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
310 }
311 throw;
312 }
313 catch( const RuntimeException & e )
314 {
315 if( cargo && isLog( cargo, LogLevel::CALL ) )
316 {
318 cargo, "except uno->py[0x" ,
319 mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
320 }
321 throw;
322 }
323 catch( const CannotConvertException & e )
324 {
325 if( isLog( cargo, LogLevel::CALL ) )
326 {
328 cargo, "except uno->py[0x" ,
329 mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
330 }
331 throw;
332 }
333 return ret;
334}
335
336void Adapter::setValue( const OUString & aPropertyName, const Any & value )
337{
338 if( !hasProperty( aPropertyName ) )
339 {
340 throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName + " is unknown." );
341 }
342
344 try
345 {
346 if( !Py_IsInitialized() )
347 throw InvocationTargetException();
348
349 Runtime runtime;
350 PyRef obj = runtime.any2PyObject( value );
351
352 // any2PyObject() can release the GIL
353 if( !Py_IsInitialized() )
354 throw InvocationTargetException();
355
356 PyObject_SetAttrString(
357 mWrappedObject.get(), TO_ASCII(aPropertyName), obj.get() );
359
360 }
361 catch( const IllegalArgumentException & exc )
362 {
363 css::uno::Any anyEx = cppu::getCaughtException();
364 throw InvocationTargetException( exc.Message, *this, anyEx );
365 }
366}
367
368Any Adapter::getValue( const OUString & aPropertyName )
369{
370 Any ret;
372 {
373 // Should probably be InvocationTargetException, but the interface doesn't allow it
374 if( !Py_IsInitialized() )
375 throw RuntimeException();
376
377 Runtime runtime;
378 PyRef pyRef(
379 PyObject_GetAttrString( mWrappedObject.get(), TO_ASCII(aPropertyName) ),
380 SAL_NO_ACQUIRE );
381
382 if (!pyRef.is() || PyErr_Occurred())
383 {
384 throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName + " is unknown." );
385 }
386 ret = runtime.pyObject2Any( pyRef );
387 }
388 return ret;
389}
390
391sal_Bool Adapter::hasMethod( const OUString & aMethodName )
392{
393 return hasProperty( aMethodName );
394}
395
396sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
397{
398 bool bRet = false;
400 {
401 // Should probably be InvocationTargetException, but the interface doesn't allow it
402 if( !Py_IsInitialized() )
403 throw RuntimeException();
404
405 bRet = PyObject_HasAttrString(
406 mWrappedObject.get() , TO_ASCII( aPropertyName ));
407 }
408 return bRet;
409}
410
411}
412
413/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const css::uno::Sequence< sal_Int8 > & getSeq() const
virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection() override
Adapter(PyRef obj, const css::uno::Sequence< css::uno::Type > &types)
MethodOutIndexMap m_methodOutIndexMap
Definition: pyuno_impl.hxx:254
virtual css::uno::Any SAL_CALL getValue(const OUString &aPropertyName) override
virtual ~Adapter() override
virtual void SAL_CALL setValue(const OUString &aPropertyName, const css::uno::Any &aValue) override
virtual sal_Bool SAL_CALL hasProperty(const OUString &aName) override
css::uno::Sequence< sal_Int16 > getOutIndexes(const OUString &functionName)
virtual css::uno::Any SAL_CALL invoke(const OUString &aFunctionName, const css::uno::Sequence< css::uno::Any > &aParams, css::uno::Sequence< sal_Int16 > &aOutParamIndex, css::uno::Sequence< css::uno::Any > &aOutParam) override
virtual sal_Bool SAL_CALL hasMethod(const OUString &aName) override
PyRef mWrappedObject
Definition: pyuno_impl.hxx:251
PyInterpreterState * mInterpreter
Definition: pyuno_impl.hxx:252
static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId()
css::uno::Sequence< css::uno::Type > mTypes
Definition: pyuno_impl.hxx:253
virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 > &aIdentifier) override
Helper class for keeping references to python objects.
Definition: pyuno.hxx:80
void scratch() noexcept
clears the reference without decreasing the reference count only seldom needed !
Definition: pyuno.hxx:131
bool is() const
returns 1 when the reference points to a python object python object, otherwise 0.
Definition: pyuno.hxx:139
PyObject * get() const noexcept
Definition: pyuno.hxx:99
PyObject * getAcquired() const
Definition: pyuno.hxx:101
helper class for attaching the current thread to the python runtime.
Definition: pyuno.hxx:272
helper class for detaching the current thread from the python runtime to do some blocking,...
Definition: pyuno.hxx:300
The pyuno::Runtime class keeps the internal state of the python UNO bridge for the currently in use p...
Definition: pyuno.hxx:164
css::uno::Any extractUnoException(const PyRef &excType, const PyRef &excValue, const PyRef &excTraceback) const
extracts a proper uno exception from a given python exception
css::uno::Any pyObject2Any(const PyRef &source, enum ConversionMode mode=REJECT_UNO_ANY) const
converts a Python object to a UNO any
PyRef any2PyObject(const css::uno::Any &source) const
converts something contained in a UNO Any to a Python object
RuntimeImpl * getImpl() const
Returns the internal handle.
Definition: pyuno.hxx:242
Any value
size
sal_Int64 getSomethingImpl(const css::uno::Sequence< sal_Int8 > &rId, T *pThis, FallbackToGetSomethingOf< Base >={})
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
Any SAL_CALL getCaughtException()
int i
const sal_Int32 CALL
Definition: pyuno_impl.hxx:74
Definition: pyuno.cxx:72
void raiseInvocationTargetExceptionWhenNeeded(const Runtime &runtime)
bool isLog(RuntimeCargo const *cargo, sal_Int32 loglevel)
Definition: pyuno_util.cxx:93
void logCall(RuntimeCargo *cargo, const char *intro, void *ptr, std::u16string_view aFunctionName, const css::uno::Sequence< css::uno::Any > &args)
void decreaseRefCount(PyInterpreterState *interpreter, PyObject *object)
releases a refcount on the interpreter object and on another given python object.
Definition: pyuno_gc.cxx:104
static StaticDestructorGuard guard
Definition: pyuno_gc.cxx:45
void logException(RuntimeCargo *cargo, const char *intro, void *ptr, std::u16string_view aFunctionName, const void *data, const css::uno::Type &type)
Definition: pyuno_util.cxx:145
OUString pyString2ustring(PyObject *str)
Definition: pyuno_util.cxx:57
@ NOT_NULL
definition of a no acquire enum for ctors
Definition: pyuno.hxx:65
void logReply(RuntimeCargo *cargo, const char *intro, void *ptr, std::u16string_view aFunctionName, const css::uno::Any &returnValue, const css::uno::Sequence< css::uno::Any > &args)
#define TO_ASCII(x)
css::uno::Reference< css::beans::XIntrospection > xIntrospection
Definition: pyuno_impl.hxx:222
css::uno::Reference< css::script::XInvocationAdapterFactory2 > xAdapterFactory
Definition: pyuno_impl.hxx:221
PyObject_HEAD struct RuntimeCargo * cargo
Definition: pyuno_impl.hxx:238
unsigned char sal_Bool