/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "pyuno_impl.hxx" #include #include #include #include using com::sun::star::beans::XIntrospectionAccess; using com::sun::star::beans::XIntrospection; using com::sun::star::uno::Any; using com::sun::star::uno::makeAny; using com::sun::star::uno::Reference; using com::sun::star::uno::Sequence; using com::sun::star::uno::RuntimeException; using com::sun::star::uno::XInterface; using com::sun::star::uno::Type; using com::sun::star::lang::XUnoTunnel; using com::sun::star::lang::IllegalArgumentException; using com::sun::star::beans::UnknownPropertyException; using com::sun::star::script::CannotConvertException; using com::sun::star::reflection::InvocationTargetException; using com::sun::star::reflection::XIdlMethod; using com::sun::star::reflection::ParamInfo; using com::sun::star::reflection::XIdlClass; #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr() namespace pyuno { Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types ) : mWrappedObject( ref ), mInterpreter( (PyThreadState_Get()->interp) ), mTypes( types ) {} Adapter::~Adapter() { // Problem: We don't know, if we have the python interpreter lock // There is no runtime function to get to know this. decreaseRefCount( mInterpreter, mWrappedObject.get() ); mWrappedObject.scratch(); } static cppu::OImplementationId g_id( sal_False ); Sequence Adapter::getUnoTunnelImplementationId() { return g_id.getImplementationId(); } sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id) throw (RuntimeException) { if( id == g_id.getImplementationId() ) return reinterpret_cast(this); return 0; } void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime ) throw ( InvocationTargetException ) { if( PyErr_Occurred() ) { PyRef excType, excValue, excTraceback; PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback); Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) ); throw InvocationTargetException( ((com::sun::star::uno::Exception*)unoExc.getValue())->Message, Reference(), unoExc ); } } Reference< XIntrospectionAccess > Adapter::getIntrospection() throw ( RuntimeException ) { // not supported return Reference< XIntrospectionAccess > (); } Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName ) { Sequence< sal_Int16 > ret; MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName ); if( ii == m_methodOutIndexMap.end() ) { Runtime runtime; { PyThreadDetach antiguard; // retrieve the adapter object again. It will be the same instance as before, // (the adapter factory keeps a weak map inside, which I couldn't have outside) Reference< XInterface > unoAdapterObject = runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes ); // uuuh, that's really expensive. The alternative would have been, to store // an instance of the introspection at (this), but this results in a cyclic // reference, which is never broken (as it is up to OOo1.1.0). Reference< XIntrospectionAccess > introspection = runtime.getImpl()->cargo->xIntrospection->inspect( makeAny( unoAdapterObject ) ); if( !introspection.is() ) { throw RuntimeException( OUString( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ), Reference< XInterface > () ); } Reference< XIdlMethod > method = introspection->getMethod( functionName, com::sun::star::beans::MethodConcept::ALL ); if( ! method.is( ) ) { throw RuntimeException( "pyuno bridge: Couldn't get reflection for method " + functionName, Reference< XInterface > () ); } Sequence< ParamInfo > seqInfo = method->getParameterInfos(); int i; int nOuts = 0; for( i = 0 ; i < seqInfo.getLength() ; i ++ ) { if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT || seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT ) { // sequence must be interpreted as return value/outparameter tuple ! nOuts ++; } } if( nOuts ) { ret.realloc( nOuts ); sal_Int32 nOutsAssigned = 0; for( i = 0 ; i < seqInfo.getLength() ; i ++ ) { if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT || seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT ) { ret[nOutsAssigned] = (sal_Int16) i; nOutsAssigned ++; } } } } // guard active again ! m_methodOutIndexMap[ functionName ] = ret; } else { ret = ii->second; } return ret; } Any Adapter::invoke( const OUString &aFunctionName, const Sequence< Any >& aParams, Sequence< sal_Int16 > &aOutParamIndex, Sequence< Any > &aOutParam) throw (IllegalArgumentException,CannotConvertException,InvocationTargetException,RuntimeException) { Any ret; // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is // always handled by the adapter directly. if( aParams.getLength() == 1 && 0 == aFunctionName.compareToAscii( "getSomething" ) ) { Sequence< sal_Int8 > id; if( aParams[0] >>= id ) return com::sun::star::uno::makeAny( getSomething( id ) ); } RuntimeCargo *cargo = 0; try { PyThreadAttach guard( mInterpreter ); { // convert parameters to python args // TODO: Out parameter Runtime runtime; cargo = runtime.getImpl()->cargo; if( isLog( cargo, LogLevel::CALL ) ) { logCall( cargo, "try uno->py[0x", mWrappedObject.get(), aFunctionName, aParams ); } sal_Int32 size = aParams.getLength(); PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE ); int i; // fill tuple with default values in case of exceptions for( i = 0 ;i < size ; i ++ ) { Py_INCREF( Py_None ); PyTuple_SetItem( argsTuple.get(), i, Py_None ); } // convert args to python for( i = 0; i < size ; i ++ ) { PyRef val = runtime.any2PyObject( aParams[i] ); PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() ); } // get callable PyRef method(PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aFunctionName)), SAL_NO_ACQUIRE); raiseInvocationTargetExceptionWhenNeeded( runtime); if( !method.is() ) { OUStringBuffer buf; buf.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName ); buf.appendAscii( " is not implemented at object " ); PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE ); buf.append(pyString2ustring(str.get())); throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 ); } PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE ); raiseInvocationTargetExceptionWhenNeeded( runtime); if( pyRet.is() ) { ret = runtime.pyObject2Any( pyRet ); if( ret.hasValue() && ret.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE && 0 != aFunctionName.compareToAscii( "getTypes" ) && // needed by introspection itself ! 0 != aFunctionName.compareToAscii( "getImplementationId" ) ) // needed by introspection itself ! { // the sequence can either be // 1) a simple sequence return value // 2) a sequence, where the first element is the return value // and the following elements are interpreted as the outparameter // I can only decide for one solution by checking the method signature, // so I need the reflection of the adapter ! aOutParamIndex = getOutIndexes( aFunctionName ); if( aOutParamIndex.getLength() ) { // out parameters exist, extract the sequence Sequence< Any > seq; if( ! ( ret >>= seq ) ) { throw RuntimeException( "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName, Reference< XInterface > () ); } if( aOutParamIndex.getLength() +1 != seq.getLength() ) { OUStringBuffer buf; buf.append( "pyuno bridge: expected for method " ); buf.append( aFunctionName ); buf.append( " one return value and " ); buf.append( (sal_Int32) aOutParamIndex.getLength() ); buf.append( " out parameters, got a sequence of " ); buf.append( seq.getLength() ); buf.append( " elements as return value." ); throw RuntimeException(buf.makeStringAndClear(), *this ); } aOutParam.realloc( aOutParamIndex.getLength() ); ret = seq[0]; for( i = 0 ; i < aOutParamIndex.getLength() ; i ++ ) { aOutParam[i] = seq[1+i]; } } // else { sequence is a return value !} } } // log the reply, if desired if( isLog( cargo, LogLevel::CALL ) ) { logReply( cargo, "success uno->py[0x" , mWrappedObject.get(), aFunctionName, ret, aOutParam ); } } } catch( const InvocationTargetException & e ) { if( isLog( cargo, LogLevel::CALL ) ) { logException( cargo, "except uno->py[0x" , mWrappedObject.get(), aFunctionName, e.TargetException.getValue(),e.TargetException.getValueType() ); } throw; } catch( const IllegalArgumentException & e ) { if( isLog( cargo, LogLevel::CALL ) ) { logException( cargo, "except uno->py[0x" , mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) ); } throw; } catch( const RuntimeException & e ) { if( cargo && isLog( cargo, LogLevel::CALL ) ) { logException( cargo, "except uno->py[0x" , mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) ); } throw; } catch( const CannotConvertException & e ) { if( isLog( cargo, LogLevel::CALL ) ) { logException( cargo, "except uno->py[0x" , mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) ); } throw; } return ret; } void Adapter::setValue( const OUString & aPropertyName, const Any & value ) throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException) { if( !hasProperty( aPropertyName ) ) { OUStringBuffer buf; buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName ); buf.appendAscii( " is unknown." ); throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () ); } PyThreadAttach guard( mInterpreter ); try { Runtime runtime; PyRef obj = runtime.any2PyObject( value ); PyObject_SetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName), obj.get() ); raiseInvocationTargetExceptionWhenNeeded( runtime); } catch( const IllegalArgumentException & exc ) { throw InvocationTargetException( exc.Message, *this, com::sun::star::uno::makeAny( exc ) ); } } Any Adapter::getValue( const OUString & aPropertyName ) throw ( UnknownPropertyException, RuntimeException ) { Any ret; PyThreadAttach guard( mInterpreter ); { Runtime runtime; PyRef pyRef( PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName) ), SAL_NO_ACQUIRE ); raiseInvocationTargetExceptionWhenNeeded( runtime); if( !pyRef.is() ) { OUStringBuffer buf; buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName ); buf.appendAscii( " is unknown." ); throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () ); } ret = runtime.pyObject2Any( pyRef ); } return ret; } sal_Bool Adapter::hasMethod( const OUString & aMethodName ) throw ( RuntimeException ) { return hasProperty( aMethodName ); } sal_Bool Adapter::hasProperty( const OUString & aPropertyName ) throw ( RuntimeException ) { bool bRet = false; PyThreadAttach guard( mInterpreter ); { bRet = PyObject_HasAttrString( mWrappedObject.get() , (char*) TO_ASCII( aPropertyName )); } return bRet; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */