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) 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 { 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 102 Reference< XIntrospectionAccess > Adapter::getIntrospection() 103 { 104 // not supported 105 return Reference< XIntrospectionAccess > (); 106 } 107 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 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 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 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 410 sal_Bool Adapter::hasMethod( const OUString & aMethodName ) 411 { 412 return hasProperty( aMethodName ); 413 } 414 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