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