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) throw (RuntimeException)
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 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
getIntrospection()103 Reference< XIntrospectionAccess > Adapter::getIntrospection()
104 throw ( RuntimeException )
105 {
106 // not supported
107 return Reference< XIntrospectionAccess > ();
108 }
109
getOutIndexes(const OUString & functionName)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
invoke(const OUString & aFunctionName,const Sequence<Any> & aParams,Sequence<sal_Int16> & aOutParamIndex,Sequence<Any> & aOutParam)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.append( pyString2ustring( 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
setValue(const OUString & aPropertyName,const Any & value)363 void Adapter::setValue( const OUString & aPropertyName, const Any & value )
364 throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException)
365 {
366 if( !hasProperty( aPropertyName ) )
367 {
368 OUStringBuffer buf;
369 buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
370 buf.appendAscii( " is unknown." );
371 throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
372 }
373
374 PyThreadAttach guard( mInterpreter );
375 try
376 {
377 Runtime runtime;
378 PyRef obj = runtime.any2PyObject( value );
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
getValue(const OUString & aPropertyName)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
hasMethod(const OUString & aMethodName)415 sal_Bool Adapter::hasMethod( const OUString & aMethodName )
416 throw ( RuntimeException )
417 {
418 return hasProperty( aMethodName );
419 }
420
hasProperty(const OUString & aPropertyName)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