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 
61 Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types )
62     : mWrappedObject( ref ),
63       mInterpreter( (PyThreadState_Get()->interp) ),
64       mTypes( types )
65 {}
66 
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 
77 Sequence<sal_Int8> Adapter::getUnoTunnelImplementationId()
78 {
79     return g_id.getImplementationId();
80 }
81 
82 sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id) throw (RuntimeException)
83 {
84     if( id == g_id.getImplementationId() )
85         return reinterpret_cast<sal_Int64>(this);
86     return 0;
87 }
88 
89 void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime )
90     throw ( InvocationTargetException )
91 {
92     if( PyErr_Occurred() )
93     {
94         PyRef excType, excValue, excTraceback;
95         PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback);
96         Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) );
97         throw InvocationTargetException(
98             ((com::sun::star::uno::Exception*)unoExc.getValue())->Message,
99             Reference<XInterface>(), unoExc );
100     }
101 }
102 
103 Reference< XIntrospectionAccess > Adapter::getIntrospection()
104     throw ( RuntimeException )
105 {
106     // not supported
107     return Reference< XIntrospectionAccess > ();
108 }
109 
110 Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName )
111 {
112     Sequence< sal_Int16 > ret;
113     MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName );
114     if( ii == m_methodOutIndexMap.end() )
115     {
116 
117         Runtime runtime;
118         {
119             PyThreadDetach antiguard;
120 
121             // retrieve the adapter object again. It will be the same instance as before,
122             // (the adapter factory keeps a weak map inside, which I couldn't have outside)
123             Reference< XInterface > unoAdapterObject =
124                 runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes );
125 
126             // uuuh, that's really expensive. The alternative would have been, to store
127             // an instance of the introspection at (this), but this results in a cyclic
128             // reference, which is never broken (as it is up to OOo1.1.0).
129             Reference< XIntrospectionAccess > introspection =
130                 runtime.getImpl()->cargo->xIntrospection->inspect( makeAny( unoAdapterObject ) );
131 
132             if( !introspection.is() )
133             {
134                 throw RuntimeException(
135                     OUString( RTL_CONSTASCII_USTRINGPARAM( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ) ),
136                     Reference< XInterface > () );
137             }
138 
139             Reference< XIdlMethod > method = introspection->getMethod(
140                 functionName, com::sun::star::beans::MethodConcept::ALL );
141             if( ! method.is( ) )
142             {
143                 throw RuntimeException(
144                     (OUString(
145                         RTL_CONSTASCII_USTRINGPARAM(
146                             "pyuno bridge: Couldn't get reflection for method "))
147                      + functionName),
148                     Reference< XInterface > () );
149             }
150 
151             Sequence< ParamInfo > seqInfo = method->getParameterInfos();
152             int i;
153             int nOuts = 0;
154             for( i = 0 ; i < seqInfo.getLength() ; i ++ )
155             {
156                 if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
157                     seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
158                 {
159                     // sequence must be interpreted as return value/outparameter tuple !
160                     nOuts ++;
161                 }
162             }
163 
164             if( nOuts )
165             {
166                 ret.realloc( nOuts );
167                 sal_Int32 nOutsAssigned = 0;
168                 for( i = 0 ; i < seqInfo.getLength() ; i ++ )
169                 {
170                     if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
171                         seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
172                     {
173                         ret[nOutsAssigned] = (sal_Int16) i;
174                         nOutsAssigned ++;
175                     }
176                 }
177             }
178         }
179         // guard active again !
180         m_methodOutIndexMap[ functionName ] = ret;
181     }
182     else
183     {
184         ret = ii->second;
185     }
186     return ret;
187 }
188 
189 Any Adapter::invoke( const OUString &aFunctionName,
190                      const Sequence< Any >& aParams,
191                      Sequence< sal_Int16 > &aOutParamIndex,
192                      Sequence< Any > &aOutParam)
193     throw (IllegalArgumentException,CannotConvertException,InvocationTargetException,RuntimeException)
194 {
195     Any ret;
196 
197     // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
198     // always handled by the adapter directly.
199     if( aParams.getLength() == 1 && 0 == aFunctionName.compareToAscii( "getSomething" ) )
200     {
201         Sequence< sal_Int8 > id;
202         if( aParams[0] >>= id )
203             return com::sun::star::uno::makeAny( getSomething( id ) );
204 
205     }
206 
207     RuntimeCargo *cargo = 0;
208     try
209     {
210     PyThreadAttach guard( mInterpreter );
211     {
212         // convert parameters to python args
213         // TODO: Out parameter
214         Runtime runtime;
215         cargo = runtime.getImpl()->cargo;
216         if( isLog( cargo, LogLevel::CALL ) )
217         {
218             logCall( cargo, "try     uno->py[0x",
219                      mWrappedObject.get(), aFunctionName, aParams );
220         }
221 
222         sal_Int32 size = aParams.getLength();
223         PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE );
224         int i;
225         // fill tuple with default values in case of exceptions
226         for(  i = 0 ;i < size ; i ++ )
227         {
228             Py_INCREF( Py_None );
229             PyTuple_SetItem( argsTuple.get(), i, Py_None );
230         }
231 
232         // convert args to python
233         for( i = 0; i < size ; i ++  )
234         {
235             PyRef val = runtime.any2PyObject( aParams[i] );
236             PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() );
237         }
238 
239         // get callable
240         PyRef method(PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aFunctionName)),
241                      SAL_NO_ACQUIRE);
242         raiseInvocationTargetExceptionWhenNeeded( runtime);
243         if( !method.is() )
244         {
245             OUStringBuffer buf;
246             buf.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName );
247             buf.appendAscii( " is not implemented at object " );
248             PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
249             buf.appendAscii( PyString_AsString( str.get() ));
250             throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 );
251         }
252 
253         PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE );
254         raiseInvocationTargetExceptionWhenNeeded( runtime);
255         if( pyRet.is() )
256         {
257             ret = runtime.pyObject2Any( pyRet );
258 
259             if( ret.hasValue() &&
260                 ret.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE &&
261                 0 != aFunctionName.compareToAscii( "getTypes" ) &&  // needed by introspection itself !
262                 0 != aFunctionName.compareToAscii( "getImplementationId" ) ) // needed by introspection itself !
263             {
264                 // the sequence can either be
265                 // 1)  a simple sequence return value
266                 // 2)  a sequence, where the first element is the return value
267                 //     and the following elements are interpreted as the outparameter
268                 // I can only decide for one solution by checking the method signature,
269                 // so I need the reflection of the adapter !
270                 aOutParamIndex = getOutIndexes( aFunctionName );
271                 if( aOutParamIndex.getLength() )
272                 {
273                     // out parameters exist, extract the sequence
274                     Sequence< Any  > seq;
275                     if( ! ( ret >>= seq ) )
276                     {
277                         throw RuntimeException(
278                             (OUString(
279                                 RTL_CONSTASCII_USTRINGPARAM(
280                                     "pyuno bridge: Couldn't extract out"
281                                     " parameters for method "))
282                              + aFunctionName),
283                             Reference< XInterface > () );
284                     }
285 
286                     if( aOutParamIndex.getLength() +1 != seq.getLength() )
287                     {
288                         OUStringBuffer buf;
289                         buf.appendAscii( "pyuno bridge: expected for method " );
290                         buf.append( aFunctionName );
291                         buf.appendAscii( " one return value and " );
292                         buf.append( (sal_Int32) aOutParamIndex.getLength() );
293                         buf.appendAscii( " out parameters, got a sequence of " );
294                         buf.append( seq.getLength() );
295                         buf.appendAscii( " elements as return value." );
296                         throw RuntimeException(buf.makeStringAndClear(), *this );
297                     }
298 
299                     aOutParam.realloc( aOutParamIndex.getLength() );
300                     ret = seq[0];
301                     for( i = 0 ; i < aOutParamIndex.getLength() ; i ++ )
302                     {
303                         aOutParam[i] = seq[1+i];
304                     }
305                 }
306                 // else { sequence is a return value !}
307             }
308         }
309 
310         // log the reply, if desired
311         if( isLog( cargo, LogLevel::CALL ) )
312         {
313             logReply( cargo, "success uno->py[0x" ,
314                       mWrappedObject.get(), aFunctionName, ret, aOutParam );
315         }
316     }
317 
318     }
319     catch(InvocationTargetException & e )
320     {
321         if( isLog( cargo, LogLevel::CALL ) )
322         {
323             logException(
324                 cargo, "except  uno->py[0x" ,
325                 mWrappedObject.get(), aFunctionName,
326                 e.TargetException.getValue(),e.TargetException.getValueType() );
327         }
328         throw;
329     }
330     catch( RuntimeException & e )
331     {
332         if( cargo && isLog( cargo, LogLevel::CALL ) )
333         {
334             logException(
335                 cargo, "except  uno->py[0x" ,
336                 mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
337         }
338         throw;
339     }
340     catch( CannotConvertException & e )
341     {
342         if( isLog( cargo, LogLevel::CALL ) )
343         {
344             logException(
345                 cargo, "except  uno->py[0x" ,
346                 mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
347         }
348         throw;
349     }
350     catch(  IllegalArgumentException & e )
351     {
352         if( isLog( cargo, LogLevel::CALL ) )
353         {
354             logException(
355                 cargo,  "except  uno->py[0x" ,
356                 mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
357         }
358         throw;
359     }
360     return ret;
361 }
362 
363 void Adapter::setValue( const OUString & aPropertyName, const Any & value )
364     throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException)
365 {
366     PyThreadAttach guard( mInterpreter );
367     try
368     {
369         Runtime runtime;
370         PyRef obj = runtime.any2PyObject( value );
371 
372         if( !hasProperty( aPropertyName ) )
373         {
374             OUStringBuffer buf;
375             buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
376             buf.appendAscii( " is unknown." );
377             throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
378         }
379 
380         PyObject_SetAttrString(
381             mWrappedObject.get(), (char*)TO_ASCII(aPropertyName), obj.get() );
382         raiseInvocationTargetExceptionWhenNeeded( runtime);
383 
384     }
385     catch( IllegalArgumentException & exc )
386     {
387         throw InvocationTargetException( exc.Message, *this, com::sun::star::uno::makeAny( exc ) );
388     }
389 }
390 
391 Any Adapter::getValue( const OUString & aPropertyName )
392     throw ( UnknownPropertyException, RuntimeException )
393 {
394     Any ret;
395     PyThreadAttach guard( mInterpreter );
396     {
397         Runtime runtime;
398         PyRef pyRef(
399             PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName) ),
400             SAL_NO_ACQUIRE );
401 
402         raiseInvocationTargetExceptionWhenNeeded( runtime);
403         if( !pyRef.is() )
404         {
405             OUStringBuffer buf;
406             buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
407             buf.appendAscii( " is unknown." );
408             throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
409         }
410         ret = runtime.pyObject2Any( pyRef );
411     }
412     return ret;
413 }
414 
415 sal_Bool Adapter::hasMethod( const OUString & aMethodName )
416     throw ( RuntimeException )
417 {
418     return hasProperty( aMethodName );
419 }
420 
421 sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
422     throw ( RuntimeException )
423 {
424     bool bRet = false;
425     PyThreadAttach guard( mInterpreter );
426     {
427         bRet = PyObject_HasAttrString(
428             mWrappedObject.get() , (char*) TO_ASCII( aPropertyName ));
429     }
430     return bRet;
431 }
432 
433 }
434