/* -*- Mode: C++; eval:(c-set-style "bsd"); 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 <config_features.h>
#include <config_folders.h>
 
#include "pyuno_impl.hxx"
 
#include <o3tl/any.hxx>
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <osl/module.h>
#include <osl/process.h>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/bootstrap.hxx>
 
#include <typelib/typedescription.hxx>
 
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/beans/XMaterialHolder.hpp>
#include <com/sun/star/beans/theIntrospection.hpp>
#include <com/sun/star/script/Converter.hpp>
#include <com/sun/star/script/InvocationAdapterFactory.hpp>
#include <com/sun/star/reflection/theCoreReflection.hpp>
#include <comphelper/sequence.hxx>
#include <cppuhelper/exc_hlp.hxx>
 
#include <vector>
 
using com::sun::star::uno::Reference;
using com::sun::star::uno::XInterface;
using com::sun::star::uno::Any;
using com::sun::star::uno::TypeDescription;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::Type;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::Exception;
using com::sun::star::uno::RuntimeException;
using com::sun::star::uno::XComponentContext;
using com::sun::star::lang::WrappedTargetRuntimeException;
using com::sun::star::lang::XSingleServiceFactory;
using com::sun::star::lang::XUnoTunnel;
using com::sun::star::reflection::theCoreReflection;
using com::sun::star::reflection::InvocationTargetException;
using com::sun::star::script::Converter;
using com::sun::star::script::XTypeConverter;
using com::sun::star::script::XInvocation;
using com::sun::star::beans::XMaterialHolder;
using com::sun::star::beans::theIntrospection;
 
namespace pyuno
{
 
static PyTypeObject RuntimeImpl_Type =
{
    PyVarObject_HEAD_INIT (&PyType_Type, 0)
    "pyuno_runtime",
    sizeof (RuntimeImpl),
    0,
    RuntimeImpl::del,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    0,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    0,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    0,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr
    , 0
#if PY_VERSION_HEX >= 0x03040000
    , nullptr
#endif
};
 
/*----------------------------------------------------------------------
  Runtime implementation
 -----------------------------------------------------------------------*/
/// @throws css::uno::RuntimeException
static void getRuntimeImpl( PyRef & globalDict, PyRef &runtimeImpl )
{
    PyThreadState * state = PyThreadState_Get();
    if( ! state )
    {
        throw RuntimeException( "python global interpreter must be held (thread must be attached)" );
    }
 
    PyObject* pModule = PyImport_AddModule("__main__");
 
    if (!pModule)
    {
        throw RuntimeException("can't import __main__ module");
    }
 
    globalDict = PyRef( PyModule_GetDict(pModule));
 
    if( ! globalDict.is() ) // FATAL !
    {
        throw RuntimeException("can't find __main__ module");
    }
    runtimeImpl = PyDict_GetItemString( globalDict.get() , "pyuno_runtime" );
}
 
/// @throws RuntimeException
static PyRef importUnoModule( )
{
    // import the uno module
    PyRef module( PyImport_ImportModule( "uno" ), SAL_NO_ACQUIRE, NOT_NULL );
    if( PyErr_Occurred() )
    {
        PyRef excType, excValue, excTraceback;
        PyErr_Fetch( reinterpret_cast<PyObject **>(&excType), reinterpret_cast<PyObject**>(&excValue), reinterpret_cast<PyObject**>(&excTraceback));
        // As of Python 2.7 this gives a rather non-useful "<traceback object at 0xADDRESS>",
        // but it is the best we can do in the absence of uno._uno_extract_printable_stacktrace
        // Who knows, a future Python might print something better.
        PyRef str( PyObject_Str( excTraceback.get() ), SAL_NO_ACQUIRE );
 
        OUStringBuffer buf;
        buf.append( "python object raised an unknown exception (" );
        PyRef valueRep( PyObject_Repr( excValue.get() ), SAL_NO_ACQUIRE );
        buf.appendAscii( PyStr_AsString( valueRep.get())).append( ", traceback follows\n" );
        buf.appendAscii( PyStr_AsString( str.get() ) );
        buf.append( ")" );
        throw RuntimeException( buf.makeStringAndClear() );
    }
    PyRef dict( PyModule_GetDict( module.get() ) );
    return dict;
}
 
static void readLoggingConfig( sal_Int32 *pLevel, FILE **ppFile )
{
    *pLevel = LogLevel::NONE;
    *ppFile = nullptr;
    OUString fileName;
    osl_getModuleURLFromFunctionAddress(
        reinterpret_cast< oslGenericFunction >(readLoggingConfig),
        &fileName.pData );
    fileName = fileName.copy( fileName.lastIndexOf( '/' )+1 );
#ifdef MACOSX
    fileName += "../" LIBO_ETC_FOLDER "/";
#endif
    fileName += SAL_CONFIGFILE("pyuno" );
    rtl::Bootstrap bootstrapHandle( fileName );
 
    OUString str;
    if( bootstrapHandle.getFrom( "PYUNO_LOGLEVEL", str ) )
    {
        if ( str == "NONE" )
            *pLevel = LogLevel::NONE;
        else if ( str == "CALL" )
            *pLevel = LogLevel::CALL;
        else if ( str == "ARGS" )
            *pLevel = LogLevel::ARGS;
        else
        {
            fprintf( stderr, "unknown loglevel %s\n",
                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
        }
    }
    if( *pLevel > LogLevel::NONE )
    {
        *ppFile = stdout;
        if( bootstrapHandle.getFrom( "PYUNO_LOGTARGET", str ) )
        {
            if ( str == "stdout" )
                *ppFile = stdout;
            else if ( str == "stderr" )
                *ppFile = stderr;
            else
            {
                oslProcessInfo data;
                data.Size = sizeof( data );
                osl_getProcessInfo(
                    nullptr , osl_Process_IDENTIFIER , &data );
                osl_getSystemPathFromFileURL( str.pData, &str.pData);
                OString o = OUStringToOString( str, osl_getThreadTextEncoding() );
                o += ".";
                o += OString::number( data.Ident );
 
                *ppFile = fopen( o.getStr() , "w" );
                if ( *ppFile )
                {
                    // do not buffer (useful if e.g. analyzing a crash)
                    setvbuf( *ppFile, nullptr, _IONBF, 0 );
                }
                else
                {
                    fprintf( stderr, "couldn't create file %s\n",
                             OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
 
                }
            }
        }
    }
}
 
/*-------------------------------------------------------------------
 RuntimeImpl implementations
 *-------------------------------------------------------------------*/
PyRef stRuntimeImpl::create( const Reference< XComponentContext > &ctx )
{
    RuntimeImpl *me = PyObject_New (RuntimeImpl, &RuntimeImpl_Type);
    if( ! me )
        throw RuntimeException( "cannot instantiate pyuno::RuntimeImpl" );
    me->cargo = nullptr;
    // must use a different struct here, as the PyObject_New
    // makes C++ unusable
    RuntimeCargo *c = new RuntimeCargo;
    readLoggingConfig( &(c->logLevel) , &(c->logFile) );
    log( c, LogLevel::CALL, "Instantiating pyuno bridge" );
 
    c->valid = true;
    c->xContext = ctx;
    c->xInvocation = Reference< XSingleServiceFactory > (
        ctx->getServiceManager()->createInstanceWithContext(
            "com.sun.star.script.Invocation",
            ctx ),
        css::uno::UNO_QUERY_THROW );
 
    c->xTypeConverter = Converter::create(ctx);
    if( ! c->xTypeConverter.is() )
        throw RuntimeException( "pyuno: couldn't instantiate typeconverter service" );
 
    c->xCoreReflection = theCoreReflection::get(ctx);
 
    c->xAdapterFactory = css::script::InvocationAdapterFactory::create(ctx);
 
    c->xIntrospection = theIntrospection::get(ctx);
 
    Any a = ctx->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager");
    a >>= c->xTdMgr;
    if( ! c->xTdMgr.is() )
        throw RuntimeException( "pyuno: couldn't retrieve typedescriptionmanager" );
 
    me->cargo =c;
    return PyRef( reinterpret_cast< PyObject * > ( me ), SAL_NO_ACQUIRE );
}
 
void  stRuntimeImpl::del(PyObject* self)
{
    RuntimeImpl *me = reinterpret_cast< RuntimeImpl * > ( self );
    if( me->cargo->logFile )
        fclose( me->cargo->logFile );
    delete me->cargo;
    PyObject_Del (self);
}
 
 
void Runtime::initialize( const Reference< XComponentContext > & ctx )
{
    PyRef globalDict, runtime;
    getRuntimeImpl( globalDict , runtime );
    RuntimeImpl *impl = reinterpret_cast< RuntimeImpl * > (runtime.get());
 
    if( runtime.is() && impl->cargo->valid )
    {
        throw RuntimeException("pyuno runtime has already been initialized before" );
    }
    PyRef keep( RuntimeImpl::create( ctx ) );
    PyDict_SetItemString( globalDict.get(), "pyuno_runtime" , keep.get() );
    Py_XINCREF( keep.get() );
}
 
 
bool Runtime::isInitialized()
{
    PyRef globalDict, runtime;
    getRuntimeImpl( globalDict , runtime );
    RuntimeImpl *impl = reinterpret_cast< RuntimeImpl * > (runtime.get());
    return runtime.is() && impl->cargo->valid;
}
 
Runtime::Runtime()
    : impl( nullptr )
{
    PyRef globalDict, runtime;
    getRuntimeImpl( globalDict , runtime );
    if( ! runtime.is() )
    {
        throw RuntimeException(
            "pyuno runtime is not initialized, "
            "(the pyuno.bootstrap needs to be called before using any uno classes)" );
    }
    impl = reinterpret_cast< RuntimeImpl * > (runtime.get());
    Py_XINCREF( runtime.get() );
}
 
Runtime::Runtime( const Runtime & r )
{
    impl = r.impl;
    Py_XINCREF( reinterpret_cast< PyObject * >(impl) );
}
 
Runtime::~Runtime()
{
    Py_XDECREF( reinterpret_cast< PyObject * >(impl) );
}
 
Runtime & Runtime::operator = ( const Runtime & r )
{
    PyRef temp( reinterpret_cast< PyObject * >(r.impl) );
    Py_XINCREF( temp.get() );
    Py_XDECREF( reinterpret_cast< PyObject * >(impl) );
    impl = r.impl;
    return *this;
}
 
PyRef Runtime::any2PyObject (const Any &a ) const
{
    if( ! impl->cargo->valid )
    {
        throw RuntimeException("pyuno runtime must be initialized before calling any2PyObject" );
    }
 
    switch (a.getValueTypeClass ())
    {
    case css::uno::TypeClass_VOID:
    {
        Py_INCREF (Py_None);
        return PyRef(Py_None);
    }
    case css::uno::TypeClass_CHAR:
    {
        sal_Unicode c = *o3tl::forceAccess<sal_Unicode>(a);
        return PyRef( PyUNO_char_new( c , *this ), SAL_NO_ACQUIRE );
    }
    case css::uno::TypeClass_BOOLEAN:
    {
        bool b;
        if ((a >>= b) && b)
            return Py_True;
        else
            return Py_False;
    }
    case css::uno::TypeClass_BYTE:
    case css::uno::TypeClass_SHORT:
    case css::uno::TypeClass_UNSIGNED_SHORT:
    case css::uno::TypeClass_LONG:
    {
        sal_Int32 l = 0;
        a >>= l;
        return PyRef( PyLong_FromLong (l), SAL_NO_ACQUIRE );
    }
    case css::uno::TypeClass_UNSIGNED_LONG:
    {
        sal_uInt32 l = 0;
        a >>= l;
        return PyRef( PyLong_FromUnsignedLong (l), SAL_NO_ACQUIRE );
    }
    case css::uno::TypeClass_HYPER:
    {
        sal_Int64 l = 0;
        a >>= l;
        return PyRef( PyLong_FromLongLong (l), SAL_NO_ACQUIRE);
    }
    case css::uno::TypeClass_UNSIGNED_HYPER:
    {
        sal_uInt64 l = 0;
        a >>= l;
        return PyRef( PyLong_FromUnsignedLongLong (l), SAL_NO_ACQUIRE);
    }
    case css::uno::TypeClass_FLOAT:
    {
        float f = 0.0;
        a >>= f;
        return PyRef(PyFloat_FromDouble (f), SAL_NO_ACQUIRE);
    }
    case css::uno::TypeClass_DOUBLE:
    {
        double d = 0.0;
        a >>= d;
        return PyRef( PyFloat_FromDouble (d), SAL_NO_ACQUIRE);
    }
    case css::uno::TypeClass_STRING:
    {
        OUString tmp_ostr;
        a >>= tmp_ostr;
        return ustring2PyUnicode( tmp_ostr );
    }
    case css::uno::TypeClass_TYPE:
    {
        Type t;
        a >>= t;
        OString o = OUStringToOString( t.getTypeName(), RTL_TEXTENCODING_ASCII_US );
        return PyRef(
            PyUNO_Type_new (
                o.getStr(),  t.getTypeClass(), *this),
            SAL_NO_ACQUIRE);
    }
    case css::uno::TypeClass_ANY:
    {
        //I don't think this can happen.
        Py_INCREF (Py_None);
        return Py_None;
    }
    case css::uno::TypeClass_ENUM:
    {
        sal_Int32 l = *static_cast<sal_Int32 const *>(a.getValue());
        TypeDescription desc( a.getValueType() );
        if( desc.is() )
        {
            desc.makeComplete();
            typelib_EnumTypeDescription *pEnumDesc =
                reinterpret_cast<typelib_EnumTypeDescription *>(desc.get());
            for( int i = 0 ; i < pEnumDesc->nEnumValues ; i ++ )
            {
                if( pEnumDesc->pEnumValues[i] == l )
                {
                    OString v = OUStringToOString( pEnumDesc->ppEnumNames[i], RTL_TEXTENCODING_ASCII_US);
                    OString e = OUStringToOString( pEnumDesc->aBase.pTypeName, RTL_TEXTENCODING_ASCII_US);
                    return PyRef( PyUNO_Enum_new(e.getStr(),v.getStr(), *this ), SAL_NO_ACQUIRE );
                }
            }
        }
        throw RuntimeException( "Any carries enum " + a.getValueType().getTypeName() +
                " with invalid value " + OUString::number(l) );
    }
    case css::uno::TypeClass_EXCEPTION:
    case css::uno::TypeClass_STRUCT:
    {
        PyRef excClass = getClass( a.getValueType().getTypeName(), *this );
        PyRef value = PyUNOStruct_new( a, getImpl()->cargo->xInvocation );
        PyRef argsTuple( PyTuple_New( 1 ) , SAL_NO_ACQUIRE, NOT_NULL );
        PyTuple_SetItem( argsTuple.get() , 0 , value.getAcquired() );
        PyRef ret( PyObject_CallObject( excClass.get() , argsTuple.get() ), SAL_NO_ACQUIRE );
        if( ! ret.is() )
        {
            throw RuntimeException( "Couldn't instantiate python representation of structured UNO type " +
                        a.getValueType().getTypeName() );
        }
 
        if( auto e = o3tl::tryAccess<css::uno::Exception>(a) )
        {
            // add the message in a standard python way !
            PyRef args( PyTuple_New( 1 ), SAL_NO_ACQUIRE, NOT_NULL );
 
            PyRef pymsg = ustring2PyString( e->Message );
            PyTuple_SetItem( args.get(), 0 , pymsg.getAcquired() );
            // the exception base functions want to have an "args" tuple,
            // which contains the message
            PyObject_SetAttrString( ret.get(), "args", args.get() );
        }
        return ret;
    }
    case css::uno::TypeClass_SEQUENCE:
    {
        Sequence<Any> s;
 
        Sequence< sal_Int8 > byteSequence;
        if( a >>= byteSequence )
        {
            // byte sequence is treated in a special way because of performance reasons
            // @since 0.9.2
            return PyRef( PyUNO_ByteSequence_new( byteSequence, *this ), SAL_NO_ACQUIRE );
        }
        else
        {
            Reference< XTypeConverter > tc = getImpl()->cargo->xTypeConverter;
            tc->convertTo (a, cppu::UnoType<decltype(s)>::get()) >>= s;
            PyRef tuple( PyTuple_New (s.getLength()), SAL_NO_ACQUIRE, NOT_NULL);
            int i=0;
            try
            {
                for ( i = 0; i < s.getLength (); i++)
                {
                    PyRef element;
                    element = any2PyObject (tc->convertTo (s[i], s[i].getValueType() ));
                    OSL_ASSERT( element.is() );
                    PyTuple_SetItem( tuple.get(), i, element.getAcquired() );
                }
            }
            catch( css::uno::Exception & )
            {
                for( ; i < s.getLength() ; i ++ )
                {
                    Py_INCREF( Py_None );
                    PyTuple_SetItem( tuple.get(), i,  Py_None );
                }
                throw;
            }
            return tuple;
        }
    }
    case css::uno::TypeClass_INTERFACE:
    {
        Reference<XInterface> tmp_interface;
        a >>= tmp_interface;
        if (!tmp_interface.is ())
            return Py_None;
 
        return PyUNO_new( a, getImpl()->cargo->xInvocation );
    }
    default:
    {
        throw RuntimeException( "Unknown UNO type class " + OUString::number(static_cast<int>(a.getValueTypeClass())) );
    }
    }
}
 
static Sequence< Type > invokeGetTypes( const Runtime & r , PyObject * o )
{
    Sequence< Type > ret;
 
    PyRef method( PyObject_GetAttrString( o , "getTypes" ), SAL_NO_ACQUIRE );
    raiseInvocationTargetExceptionWhenNeeded( r );
    if( method.is() && PyCallable_Check( method.get() ) )
    {
        PyRef types( PyObject_CallObject( method.get(), nullptr ) , SAL_NO_ACQUIRE );
        raiseInvocationTargetExceptionWhenNeeded( r );
        if( types.is() && PyTuple_Check( types.get() ) )
        {
            int size = PyTuple_Size( types.get() );
 
            // add the XUnoTunnel interface  for uno object identity concept (hack)
            ret.realloc( size + 1 );
            for( int i = 0 ; i < size ; i ++ )
            {
                Any a = r.pyObject2Any(PyTuple_GetItem(types.get(),i));
                a >>= ret[i];
            }
            ret[size] = cppu::UnoType<css::lang::XUnoTunnel>::get();
        }
    }
    return ret;
}
 
static OUString
lcl_ExceptionMessage(PyObject *const o, OUString const*const pWrapped)
{
    OUStringBuffer buf;
    buf.append("Couldn't convert ");
    PyRef reprString( PyObject_Str(o), SAL_NO_ACQUIRE );
    buf.appendAscii( PyStr_AsString(reprString.get()) );
    buf.append(" to a UNO type");
    if (pWrapped)
    {
        buf.append("; caught exception: ");
        buf.append(*pWrapped);
    }
    return buf.makeStringAndClear();
}
 
// For Python 2.7 - see https://bugs.python.org/issue24161
// Fills aSeq and returns true if pObj is a valid iterator
bool Runtime::pyIterUnpack( PyObject *const pObj, Any &a ) const
{
    if( !PyIter_Check( pObj ))
        return false;
 
    PyObject *pItem = PyIter_Next( pObj );
    if( !pItem )
    {
        if( PyErr_Occurred() )
        {
            PyErr_Clear();
            return false;
        }
        return true;
    }
 
    ::std::vector<Any> items;
    do
    {
        PyRef rItem( pItem, SAL_NO_ACQUIRE );
        items.push_back( pyObject2Any( rItem.get() ) );
        pItem = PyIter_Next( pObj );
    }
    while( pItem );
    a <<= comphelper::containerToSequence(items);
    return true;
}
 
Any Runtime::pyObject2Any ( const PyRef & source, enum ConversionMode mode ) const
{
    if( ! impl->cargo->valid )
    {
        throw RuntimeException("pyuno runtime must be initialized before calling any2PyObject" );
    }
 
    Any a;
    PyObject *o = source.get();
    if( Py_None == o )
    {
 
    }
    // In Python 3, there is no PyInt type.
#if PY_MAJOR_VERSION < 3
    else if (PyInt_Check (o))
    {
        if( o == Py_True )
        {
            a <<= true;
        }
        else if ( o == Py_False )
        {
            a <<= false;
        }
        else
        {
            sal_Int32 l = (sal_Int32) PyLong_AsLong( o );
            if( l < 128 && l >= -128 )
            {
                sal_Int8 b = (sal_Int8 ) l;
                a <<= b;
            }
            else if( l <= 0x7fff && l >= -0x8000 )
            {
                sal_Int16 s = (sal_Int16) l;
                a <<= s;
            }
            else
            {
                a <<= l;
            }
        }
    }
#endif /* PY_MAJOR_VERSION < 3 */
    else if (PyLong_Check (o))
    {
#if PY_MAJOR_VERSION >= 3
        // Convert the Python 3 booleans that are actually of type PyLong.
        if(o == Py_True)
        {
            a <<= true;
        }
        else if(o == Py_False)
        {
            a <<= false;
        }
        else
        {
#endif /* PY_MAJOR_VERSION >= 3 */
        sal_Int64 l = static_cast<sal_Int64>(PyLong_AsLong (o));
        if( l < 128 && l >= -128 )
        {
            sal_Int8 b = static_cast<sal_Int8>(l);
            a <<= b;
        }
        else if( l <= 0x7fff && l >= -0x8000 )
        {
            sal_Int16 s = static_cast<sal_Int16>(l);
            a <<= s;
        }
        else if( l <= SAL_CONST_INT64(0x7fffffff) &&
                 l >= -SAL_CONST_INT64(0x80000000) )
        {
            sal_Int32 l32 = static_cast<sal_Int32>(l);
            a <<= l32;
        }
        else
        {
            a <<= l;
        }
#if PY_MAJOR_VERSION >= 3
        }
#endif
    }
    else if (PyFloat_Check (o))
    {
        double d = PyFloat_AsDouble (o);
        a <<= d;
    }
    else if (PyStrBytes_Check(o) || PyUnicode_Check(o))
    {
        a <<= pyString2ustring(o);
    }
    else if (PyTuple_Check (o))
    {
        Sequence<Any> s (PyTuple_Size (o));
        for (Py_ssize_t i = 0; i < PyTuple_Size (o); i++)
        {
            s[i] = pyObject2Any (PyTuple_GetItem (o, i), mode );
        }
        a <<= s;
    }
    else if (PyList_Check (o))
    {
        Py_ssize_t l = PyList_Size (o);
        Sequence<Any> s (l);
        for (Py_ssize_t i = 0; i < l; i++)
        {
            s[i] = pyObject2Any (PyList_GetItem (o, i), mode );
        }
        a <<= s;
    }
    else if (!pyIterUnpack (o, a))
    {
        Runtime runtime;
        // should be removed, in case ByteSequence gets derived from String
        if( PyObject_IsInstance( o, getByteSequenceClass( runtime ).get() ) )
        {
            PyRef str(PyObject_GetAttrString( o , "value" ),SAL_NO_ACQUIRE);
            Sequence< sal_Int8 > seq;
            if( PyStrBytes_Check( str.get() ) )
            {
                seq = Sequence<sal_Int8 > (
                    reinterpret_cast<sal_Int8*>(PyStrBytes_AsString(str.get())), PyStrBytes_Size(str.get()));
            }
            a <<= seq;
        }
        else
        if( PyObject_IsInstance( o, getTypeClass( runtime ).get() ) )
        {
            Type t = PyType2Type( o );
            a <<= t;
        }
        else if( PyObject_IsInstance( o, getEnumClass( runtime ).get() ) )
        {
            a = PyEnum2Enum( o );
        }
        else if( isInstanceOfStructOrException( o ) )
        {
            PyRef struc(PyObject_GetAttrString( o , "value" ),SAL_NO_ACQUIRE);
            PyUNO * obj = reinterpret_cast<PyUNO*>(struc.get());
            Reference< XMaterialHolder > holder( obj->members->xInvocation, UNO_QUERY );
            if( !holder.is( ) )
            {
                throw RuntimeException(
                    "struct or exception wrapper does not support XMaterialHolder" );
            }
 
            a = holder->getMaterial();
 
        }
        else if( PyObject_IsInstance( o, getPyUnoClass().get() ) )
        {
            PyUNO* o_pi = reinterpret_cast<PyUNO*>(o);
            a = o_pi->members->wrappedObject;
        }
        else if( PyObject_IsInstance( o, getPyUnoStructClass().get() ) )
        {
            PyUNO* o_pi = reinterpret_cast<PyUNO*>(o);
            Reference<XMaterialHolder> my_mh (o_pi->members->xInvocation, css::uno::UNO_QUERY_THROW);
            a = my_mh->getMaterial();
        }
        else if( PyObject_IsInstance( o, getCharClass( runtime ).get() ) )
        {
            a <<= PyChar2Unicode( o );
        }
        else if( PyObject_IsInstance( o, getAnyClass( runtime ).get() ) )
        {
            if( ACCEPT_UNO_ANY != mode )
            {
                throw RuntimeException(
                    "uno.Any instance not accepted during method call, "
                    "use uno.invoke instead" );
            }
 
            a = pyObject2Any( PyRef( PyObject_GetAttrString( o , "value" ), SAL_NO_ACQUIRE) );
            Type t;
            pyObject2Any( PyRef( PyObject_GetAttrString( o, "type" ), SAL_NO_ACQUIRE ) ) >>= t;
 
            try
            {
                a = getImpl()->cargo->xTypeConverter->convertTo( a, t );
            }
            catch( const css::uno::Exception & e )
            {
                css::uno::Any anyEx = cppu::getCaughtException();
                throw WrappedTargetRuntimeException(
                        e.Message, e.Context, anyEx);
            }
 
        }
        else
        {
            Reference< XInterface > mappedObject;
            Reference< XInvocation > adapterObject;
 
            // instance already mapped out to the world ?
            PyRef2Adapter::iterator ii = impl->cargo->mappedObjects.find( PyRef( o ) );
            if( ii != impl->cargo->mappedObjects.end() )
            {
                adapterObject = ii->second;
            }
 
            if( adapterObject.is() )
            {
                // object got already bridged !
                Reference< css::lang::XUnoTunnel > tunnel( adapterObject, UNO_QUERY );
 
                Adapter *pAdapter = reinterpret_cast<Adapter*>(
                        tunnel->getSomething(
                            ::pyuno::Adapter::getUnoTunnelImplementationId() ) );
 
                mappedObject = impl->cargo->xAdapterFactory->createAdapter(
                    adapterObject, pAdapter->getWrappedTypes() );
            }
            else
            {
                try {
                    Sequence<Type> interfaces = invokeGetTypes(*this, o);
                    if (interfaces.getLength())
                    {
                        Adapter *pAdapter = new Adapter( o, interfaces );
                        mappedObject =
                            getImpl()->cargo->xAdapterFactory->createAdapter(
                                pAdapter, interfaces );
 
                        // keep a list of exported objects to ensure object identity !
                        impl->cargo->mappedObjects[ PyRef(o) ] =
                            css::uno::WeakReference< XInvocation > ( pAdapter );
                    }
                } catch (InvocationTargetException const& e) {
                    OUString const msg(lcl_ExceptionMessage(o, &e.Message));
                    throw WrappedTargetRuntimeException( // re-wrap that
                            msg, e.Context, e.TargetException);
                }
            }
            if( mappedObject.is() )
            {
                a <<= mappedObject;
            }
            else
            {
                OUString const msg(lcl_ExceptionMessage(o, nullptr));
                throw RuntimeException(msg);
            }
        }
    }
    return a;
}
 
Any Runtime::extractUnoException( const PyRef & excType, const PyRef &excValue, const PyRef &excTraceback) const
{
    OUString str;
    Any ret;
    if( excTraceback.is() )
    {
        Exception e;
        PyRef unoModule;
        if ( impl )
        {
            try
            {
                unoModule = impl->cargo->getUnoModule();
            }
            catch (const Exception &ei)
            {
                e=ei;
            }
        }
        if( unoModule.is() )
        {
            PyRef extractTraceback(
                PyDict_GetItemString(unoModule.get(),"_uno_extract_printable_stacktrace" ) );
 
            if( PyCallable_Check(extractTraceback.get()) )
            {
                PyRef args( PyTuple_New( 1), SAL_NO_ACQUIRE, NOT_NULL );
                PyTuple_SetItem( args.get(), 0, excTraceback.getAcquired() );
                PyRef pyStr( PyObject_CallObject( extractTraceback.get(),args.get() ), SAL_NO_ACQUIRE);
                str = OUString::createFromAscii( PyStr_AsString(pyStr.get()) );
            }
            else
            {
                str = "Couldn't find uno._uno_extract_printable_stacktrace";
            }
        }
        else
        {
            str = "Could not load uno.py, no stacktrace available";
            if ( !e.Message.isEmpty() )
            {
                str += " (Error loading uno.py: " + e.Message + ")";
            }
        }
 
    }
    else
    {
        // it may occur, that no traceback is given (e.g. only native code below)
        str = "no traceback available";
    }
 
    if( isInstanceOfStructOrException( excValue.get() ) )
    {
        ret = pyObject2Any( excValue );
    }
    else
    {
        OUStringBuffer buf;
        PyRef typeName( PyObject_Str( excType.get() ), SAL_NO_ACQUIRE );
        if( typeName.is() )
        {
            buf.appendAscii( PyStr_AsString( typeName.get() ) );
        }
        else
        {
            buf.append( "no typename available" );
        }
        buf.append( ": " );
        PyRef valueRep( PyObject_Str( excValue.get() ), SAL_NO_ACQUIRE );
        if( valueRep.is() )
        {
            buf.appendAscii( PyStr_AsString( valueRep.get()));
        }
        else
        {
            buf.append( "Couldn't convert exception value to a string" );
        }
        buf.append( ", traceback follows\n" );
        if( !str.isEmpty() )
        {
            buf.append( str );
            buf.append( "\n" );
        }
        else
        {
            buf.append( ", no traceback available\n" );
        }
        RuntimeException e;
        e.Message = buf.makeStringAndClear();
#if OSL_DEBUG_LEVEL > 0
        fprintf( stderr, "Python exception: %s\n",
                 OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr() );
#endif
        ret <<= e;
    }
    return ret;
}
 
 
PyThreadAttach::PyThreadAttach( PyInterpreterState *interp)
{
    tstate = PyThreadState_New( interp );
    if( !tstate  )
        throw RuntimeException( "Couldn't create a pythreadstate" );
    PyEval_AcquireThread( tstate);
}
 
PyThreadAttach::~PyThreadAttach()
{
    PyThreadState_Clear( tstate );
    PyEval_ReleaseThread( tstate );
    PyThreadState_Delete( tstate );
}
 
PyThreadDetach::PyThreadDetach()
{
    tstate = PyThreadState_Get();
    PyEval_ReleaseThread( tstate );
}
 
    /** Acquires the global interpreter lock again
 
    */
PyThreadDetach::~PyThreadDetach()
{
    PyEval_AcquireThread( tstate );
}
 
 
PyRef const & RuntimeCargo::getUnoModule()
{
    if( ! dictUnoModule.is() )
    {
        dictUnoModule = importUnoModule();
    }
    return dictUnoModule;
}
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V560 A part of conditional expression is always true: l <= 0x7fffffffi64.

V560 A part of conditional expression is always true: l >= - 0x80000000i64.