Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / pyuno / source / module / pyuno_adapter.cxx
bloba8efd415383171d834527eddcd383f5113594e30
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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()
52 namespace pyuno
55 Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types )
56 : mWrappedObject( ref ),
57 mInterpreter( (PyThreadState_Get()->interp) ),
58 mTypes( types )
61 Adapter::~Adapter()
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);
80 return 0;
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()
101 // not supported
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() )
112 Runtime runtime;
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 );
135 if( ! method.is( ) )
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;
157 else
159 ret = ii->second;
161 return ret;
164 Any Adapter::invoke( const OUString &aFunctionName,
165 const Sequence< Any >& aParams,
166 Sequence< sal_Int16 > &aOutParamIndex,
167 Sequence< Any > &aOutParam)
169 Any ret;
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
191 Runtime runtime;
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 );
201 int i;
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() );
221 // get callable
222 PyRef method(PyObject_GetAttrString( mWrappedObject.get(), TO_ASCII(aFunctionName)),
223 SAL_NO_ACQUIRE);
225 raiseInvocationTargetExceptionWhenNeeded( runtime);
226 if( !method.is() )
228 PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
230 OUString sMsg = "pyuno::Adapter: Method "
231 + aFunctionName
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);
239 if( pyRet.is() )
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
258 Sequence< Any > seq;
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 "
269 + aFunctionName
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 );
279 ret = seq[0];
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 ) )
299 logException(
300 cargo, "except uno->py[0x" ,
301 mWrappedObject.get(), aFunctionName,
302 e.TargetException.getValue(),e.TargetException.getValueType() );
304 throw;
306 catch( const IllegalArgumentException & e )
308 if( isLog( cargo, LogLevel::CALL ) )
310 logException(
311 cargo, "except uno->py[0x" ,
312 mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
314 throw;
316 catch( const RuntimeException & e )
318 if( cargo && isLog( cargo, LogLevel::CALL ) )
320 logException(
321 cargo, "except uno->py[0x" ,
322 mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
324 throw;
326 catch( const CannotConvertException & e )
328 if( isLog( cargo, LogLevel::CALL ) )
330 logException(
331 cargo, "except uno->py[0x" ,
332 mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
334 throw;
336 return ret;
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();
352 Runtime runtime;
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 )
373 Any ret;
374 PyThreadAttach guard( mInterpreter );
376 // Should probably be InvocationTargetException, but the interface doesn't allow it
377 if( !Py_IsInitialized() )
378 throw RuntimeException();
380 Runtime runtime;
381 PyRef pyRef(
382 PyObject_GetAttrString( mWrappedObject.get(), TO_ASCII(aPropertyName) ),
383 SAL_NO_ACQUIRE );
385 if (!pyRef.is() || PyErr_Occurred())
387 throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName + " is unknown." );
389 ret = runtime.pyObject2Any( pyRef );
391 return ret;
394 sal_Bool Adapter::hasMethod( const OUString & aMethodName )
396 return hasProperty( aMethodName );
399 sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
401 bool bRet = false;
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 ));
411 return bRet;
416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */