xref: /trunk/main/pyuno/source/module/pyuno_adapter.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 #include "pyuno_impl.hxx"
24 
25 #include <rtl/ustrbuf.hxx>
26 #include <rtl/strbuf.hxx>
27 
28 #include <com/sun/star/beans/MethodConcept.hpp>
29 
30 #include <cppuhelper/typeprovider.hxx>
31 
32 using rtl::OUStringToOString;
33 using rtl::OUString;
34 using rtl::OUStringBuffer;
35 using rtl::OString;
36 using rtl::OStringBuffer;
37 
38 using com::sun::star::beans::XIntrospectionAccess;
39 using com::sun::star::beans::XIntrospection;
40 using com::sun::star::uno::Any;
41 using com::sun::star::uno::makeAny;
42 using com::sun::star::uno::Reference;
43 using com::sun::star::uno::Sequence;
44 using com::sun::star::uno::RuntimeException;
45 using com::sun::star::uno::XInterface;
46 using com::sun::star::uno::Type;
47 using com::sun::star::lang::XUnoTunnel;
48 using com::sun::star::lang::IllegalArgumentException;
49 using com::sun::star::beans::UnknownPropertyException;
50 using com::sun::star::script::CannotConvertException;
51 using com::sun::star::reflection::InvocationTargetException;
52 using com::sun::star::reflection::XIdlMethod;
53 using com::sun::star::reflection::ParamInfo;
54 using com::sun::star::reflection::XIdlClass;
55 
56 #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
57 
58 namespace pyuno
59 {
60 
Adapter(const PyRef & ref,const Sequence<Type> & types)61 Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types )
62     : mWrappedObject( ref ),
63       mInterpreter( (PyThreadState_Get()->interp) ),
64       mTypes( types )
65 {}
66 
~Adapter()67 Adapter::~Adapter()
68 {
69     // Problem: We don't know, if we have the python interpreter lock
70     //       There is no runtime function to get to know this.
71     decreaseRefCount( mInterpreter, mWrappedObject.get() );
72     mWrappedObject.scratch();
73 }
74 
75 static cppu::OImplementationId g_id( sal_False );
76 
getUnoTunnelImplementationId()77 Sequence<sal_Int8> Adapter::getUnoTunnelImplementationId()
78 {
79     return g_id.getImplementationId();
80 }
81 
getSomething(const Sequence<sal_Int8> & id)82 sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id)
83 {
84     if( id == g_id.getImplementationId() )
85         return reinterpret_cast<sal_Int64>(this);
86     return 0;
87 }
88 
raiseInvocationTargetExceptionWhenNeeded(const Runtime & runtime)89 void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime )
90 {
91     if( PyErr_Occurred() )
92     {
93         PyRef excType, excValue, excTraceback;
94         PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback);
95         Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) );
96         throw InvocationTargetException(
97             ((com::sun::star::uno::Exception*)unoExc.getValue())->Message,
98             Reference<XInterface>(), unoExc );
99     }
100 }
101 
getIntrospection()102 Reference< XIntrospectionAccess > Adapter::getIntrospection()
103 {
104     // not supported
105     return Reference< XIntrospectionAccess > ();
106 }
107 
getOutIndexes(const OUString & functionName)108 Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName )
109 {
110     Sequence< sal_Int16 > ret;
111     MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName );
112     if( ii == m_methodOutIndexMap.end() )
113     {
114 
115         Runtime runtime;
116         {
117             PyThreadDetach antiguard;
118 
119             // retrieve the adapter object again. It will be the same instance as before,
120             // (the adapter factory keeps a weak map inside, which I couldn't have outside)
121             Reference< XInterface > unoAdapterObject =
122                 runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes );
123 
124             // uuuh, that's really expensive. The alternative would have been, to store
125             // an instance of the introspection at (this), but this results in a cyclic
126             // reference, which is never broken (as it is up to OOo1.1.0).
127             Reference< XIntrospectionAccess > introspection =
128                 runtime.getImpl()->cargo->xIntrospection->inspect( makeAny( unoAdapterObject ) );
129 
130             if( !introspection.is() )
131             {
132                 throw RuntimeException(
133                     OUString( RTL_CONSTASCII_USTRINGPARAM( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ) ),
134                     Reference< XInterface > () );
135             }
136 
137             Reference< XIdlMethod > method = introspection->getMethod(
138                 functionName, com::sun::star::beans::MethodConcept::ALL );
139             if( ! method.is( ) )
140             {
141                 throw RuntimeException(
142                     (OUString(
143                         RTL_CONSTASCII_USTRINGPARAM(
144                             "pyuno bridge: Couldn't get reflection for method "))
145                      + functionName),
146                     Reference< XInterface > () );
147             }
148 
149             Sequence< ParamInfo > seqInfo = method->getParameterInfos();
150             int i;
151             int nOuts = 0;
152             for( i = 0 ; i < seqInfo.getLength() ; i ++ )
153             {
154                 if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
155                     seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
156                 {
157                     // sequence must be interpreted as return value/outparameter tuple !
158                     nOuts ++;
159                 }
160             }
161 
162             if( nOuts )
163             {
164                 ret.realloc( nOuts );
165                 sal_Int32 nOutsAssigned = 0;
166                 for( i = 0 ; i < seqInfo.getLength() ; i ++ )
167                 {
168                     if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
169                         seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
170                     {
171                         ret[nOutsAssigned] = (sal_Int16) i;
172                         nOutsAssigned ++;
173                     }
174                 }
175             }
176         }
177         // guard active again !
178         m_methodOutIndexMap[ functionName ] = ret;
179     }
180     else
181     {
182         ret = ii->second;
183     }
184     return ret;
185 }
186 
invoke(const OUString & aFunctionName,const Sequence<Any> & aParams,Sequence<sal_Int16> & aOutParamIndex,Sequence<Any> & aOutParam)187 Any Adapter::invoke( const OUString &aFunctionName,
188                      const Sequence< Any >& aParams,
189                      Sequence< sal_Int16 > &aOutParamIndex,
190                      Sequence< Any > &aOutParam)
191 {
192     Any ret;
193 
194     // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
195     // always handled by the adapter directly.
196     if( aParams.getLength() == 1 && 0 == aFunctionName.compareToAscii( "getSomething" ) )
197     {
198         Sequence< sal_Int8 > id;
199         if( aParams[0] >>= id )
200             return com::sun::star::uno::makeAny( getSomething( id ) );
201 
202     }
203 
204     RuntimeCargo *cargo = 0;
205     try
206     {
207     PyThreadAttach guard( mInterpreter );
208     {
209         // convert parameters to python args
210         // TODO: Out parameter
211         Runtime runtime;
212         cargo = runtime.getImpl()->cargo;
213         if( isLog( cargo, LogLevel::CALL ) )
214         {
215             logCall( cargo, "try     uno->py[0x",
216                      mWrappedObject.get(), aFunctionName, aParams );
217         }
218 
219         sal_Int32 size = aParams.getLength();
220         PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE );
221         int i;
222         // fill tuple with default values in case of exceptions
223         for(  i = 0 ;i < size ; i ++ )
224         {
225             Py_INCREF( Py_None );
226             PyTuple_SetItem( argsTuple.get(), i, Py_None );
227         }
228 
229         // convert args to python
230         for( i = 0; i < size ; i ++  )
231         {
232             PyRef val = runtime.any2PyObject( aParams[i] );
233             PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() );
234         }
235 
236         // get callable
237         PyRef method(PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aFunctionName)),
238                      SAL_NO_ACQUIRE);
239         raiseInvocationTargetExceptionWhenNeeded( runtime);
240         if( !method.is() )
241         {
242             OUStringBuffer buf;
243             buf.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName );
244             buf.appendAscii( " is not implemented at object " );
245             PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
246             buf.append( pyString2ustring( str.get() ) );
247             throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 );
248         }
249 
250         PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE );
251         raiseInvocationTargetExceptionWhenNeeded( runtime);
252         if( pyRet.is() )
253         {
254             ret = runtime.pyObject2Any( pyRet );
255 
256             if( ret.hasValue() &&
257                 ret.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE &&
258                 0 != aFunctionName.compareToAscii( "getTypes" ) &&  // needed by introspection itself !
259                 0 != aFunctionName.compareToAscii( "getImplementationId" ) ) // needed by introspection itself !
260             {
261                 // the sequence can either be
262                 // 1)  a simple sequence return value
263                 // 2)  a sequence, where the first element is the return value
264                 //     and the following elements are interpreted as the outparameter
265                 // I can only decide for one solution by checking the method signature,
266                 // so I need the reflection of the adapter !
267                 aOutParamIndex = getOutIndexes( aFunctionName );
268                 if( aOutParamIndex.getLength() )
269                 {
270                     // out parameters exist, extract the sequence
271                     Sequence< Any  > seq;
272                     if( ! ( ret >>= seq ) )
273                     {
274                         throw RuntimeException(
275                             (OUString(
276                                 RTL_CONSTASCII_USTRINGPARAM(
277                                     "pyuno bridge: Couldn't extract out"
278                                     " parameters for method "))
279                              + aFunctionName),
280                             Reference< XInterface > () );
281                     }
282 
283                     if( aOutParamIndex.getLength() +1 != seq.getLength() )
284                     {
285                         OUStringBuffer buf;
286                         buf.appendAscii( "pyuno bridge: expected for method " );
287                         buf.append( aFunctionName );
288                         buf.appendAscii( " one return value and " );
289                         buf.append( (sal_Int32) aOutParamIndex.getLength() );
290                         buf.appendAscii( " out parameters, got a sequence of " );
291                         buf.append( seq.getLength() );
292                         buf.appendAscii( " elements as return value." );
293                         throw RuntimeException(buf.makeStringAndClear(), *this );
294                     }
295 
296                     aOutParam.realloc( aOutParamIndex.getLength() );
297                     ret = seq[0];
298                     for( i = 0 ; i < aOutParamIndex.getLength() ; i ++ )
299                     {
300                         aOutParam[i] = seq[1+i];
301                     }
302                 }
303                 // else { sequence is a return value !}
304             }
305         }
306 
307         // log the reply, if desired
308         if( isLog( cargo, LogLevel::CALL ) )
309         {
310             logReply( cargo, "success uno->py[0x" ,
311                       mWrappedObject.get(), aFunctionName, ret, aOutParam );
312         }
313     }
314 
315     }
316     catch(InvocationTargetException & e )
317     {
318         if( isLog( cargo, LogLevel::CALL ) )
319         {
320             logException(
321                 cargo, "except  uno->py[0x" ,
322                 mWrappedObject.get(), aFunctionName,
323                 e.TargetException.getValue(),e.TargetException.getValueType() );
324         }
325         throw;
326     }
327     catch( RuntimeException & e )
328     {
329         if( cargo && isLog( cargo, LogLevel::CALL ) )
330         {
331             logException(
332                 cargo, "except  uno->py[0x" ,
333                 mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
334         }
335         throw;
336     }
337     catch( CannotConvertException & e )
338     {
339         if( isLog( cargo, LogLevel::CALL ) )
340         {
341             logException(
342                 cargo, "except  uno->py[0x" ,
343                 mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
344         }
345         throw;
346     }
347     catch(  IllegalArgumentException & e )
348     {
349         if( isLog( cargo, LogLevel::CALL ) )
350         {
351             logException(
352                 cargo,  "except  uno->py[0x" ,
353                 mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
354         }
355         throw;
356     }
357     return ret;
358 }
359 
setValue(const OUString & aPropertyName,const Any & value)360 void Adapter::setValue( const OUString & aPropertyName, const Any & value )
361 {
362     if( !hasProperty( aPropertyName ) )
363     {
364         OUStringBuffer buf;
365         buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
366         buf.appendAscii( " is unknown." );
367         throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
368     }
369 
370     PyThreadAttach guard( mInterpreter );
371     try
372     {
373         Runtime runtime;
374         PyRef obj = runtime.any2PyObject( value );
375 
376         PyObject_SetAttrString(
377             mWrappedObject.get(), (char*)TO_ASCII(aPropertyName), obj.get() );
378         raiseInvocationTargetExceptionWhenNeeded( runtime);
379 
380     }
381     catch( IllegalArgumentException & exc )
382     {
383         throw InvocationTargetException( exc.Message, *this, com::sun::star::uno::makeAny( exc ) );
384     }
385 }
386 
getValue(const OUString & aPropertyName)387 Any Adapter::getValue( const OUString & aPropertyName )
388 {
389     Any ret;
390     PyThreadAttach guard( mInterpreter );
391     {
392         Runtime runtime;
393         PyRef pyRef(
394             PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName) ),
395             SAL_NO_ACQUIRE );
396 
397         raiseInvocationTargetExceptionWhenNeeded( runtime);
398         if( !pyRef.is() )
399         {
400             OUStringBuffer buf;
401             buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
402             buf.appendAscii( " is unknown." );
403             throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
404         }
405         ret = runtime.pyObject2Any( pyRef );
406     }
407     return ret;
408 }
409 
hasMethod(const OUString & aMethodName)410 sal_Bool Adapter::hasMethod( const OUString & aMethodName )
411 {
412     return hasProperty( aMethodName );
413 }
414 
hasProperty(const OUString & aPropertyName)415 sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
416 {
417     bool bRet = false;
418     PyThreadAttach guard( mInterpreter );
419     {
420         bRet = PyObject_HasAttrString(
421             mWrappedObject.get() , (char*) TO_ASCII( aPropertyName ));
422     }
423     return bRet;
424 }
425 
426 }
427