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