<commit_before>/*************************************************************************
 *
 *  $RCSfile: opump.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: jbu $ $Date: 2001-06-22 16:32:57 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>

#include <osl/diagnose.h>

#include <com/sun/star/io/XActiveDataSource.hpp>
#include <com/sun/star/io/XActiveDataSink.hpp>
#include <com/sun/star/io/XActiveDataControl.hpp>
#include <com/sun/star/io/XConnectable.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>

#include <uno/dispatcher.h>
#include <uno/mapping.hxx>
#include <cppuhelper/implbase5.hxx>
#include <cppuhelper/factory.hxx>
#include <osl/mutex.hxx>
#include <osl/thread.h>
#include <list>

using namespace osl;
using namespace std;
using namespace rtl;
using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::registry;
using namespace com::sun::star::io;

#include "factreg.hxx"

namespace io_stm {

    class Pump : public WeakImplHelper5<
          XActiveDataSource, XActiveDataSink, XActiveDataControl, XConnectable, XServiceInfo >
    {
        Mutex                                   m_aMutex;
        oslThread                               m_aThread;

        Reference< XConnectable >               m_xPred;
        Reference< XConnectable >               m_xSucc;
        Reference< XInputStream >               m_xInput;
        Reference< XOutputStream >              m_xOutput;
        list< Reference< XStreamListener > >    m_aListeners;

        void run();
        static void static_run( void* pObject );

        void close();
        void fireError( list< Reference< XStreamListener > > & , Any & exception);

    public:
        Pump();
        virtual ~Pump();

        // XActiveDataSource
        virtual void SAL_CALL setOutputStream( const Reference< ::com::sun::star::io::XOutputStream >& xOutput ) throw();
        virtual Reference< ::com::sun::star::io::XOutputStream > SAL_CALL getOutputStream() throw();

        // XActiveDataSink
        virtual void SAL_CALL setInputStream( const Reference< ::com::sun::star::io::XInputStream >& xStream ) throw();
        virtual Reference< ::com::sun::star::io::XInputStream > SAL_CALL getInputStream() throw();

        // XActiveDataControl
        virtual void SAL_CALL addListener( const Reference< ::com::sun::star::io::XStreamListener >& xListener ) throw();
        virtual void SAL_CALL removeListener( const Reference< ::com::sun::star::io::XStreamListener >& xListener ) throw();
        virtual void SAL_CALL start() throw();
        virtual void SAL_CALL terminate() throw();

        // XConnectable
        virtual void SAL_CALL setPredecessor( const Reference< ::com::sun::star::io::XConnectable >& xPred ) throw();
        virtual Reference< ::com::sun::star::io::XConnectable > SAL_CALL getPredecessor() throw();
        virtual void SAL_CALL setSuccessor( const Reference< ::com::sun::star::io::XConnectable >& xSucc ) throw();
        virtual Reference< ::com::sun::star::io::XConnectable > SAL_CALL getSuccessor() throw();

    public: // XServiceInfo
        virtual OUString    SAL_CALL getImplementationName() throw(  );
        virtual Sequence< OUString > SAL_CALL getSupportedServiceNames(void) throw(  );
        virtual sal_Bool     SAL_CALL supportsService(const OUString& ServiceName) throw(  );
    };

Pump::Pump()
    : m_aThread( NULL )
{
    g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
}

Pump::~Pump()
{
    // exit gracefully
    osl_joinWithThread( m_aThread );
    osl_destroyThread( m_aThread );
    g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}

void Pump::fireError( list< Reference< XStreamListener > > &aList , Any & exception )
{
    list< Reference< XStreamListener > > aLocalListeners = aList;
    list< Reference< XStreamListener > >::iterator it;
    for( it = aLocalListeners.begin();
         it != aLocalListeners.end(); ++it )
    {
        try
        {
            (*it)->error( exception );
        }
        catch ( RuntimeException &e )
        {
            OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
            OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners", sMessage.getStr() );
        }
    }
}

void Pump::close()
{
    // close streams and release references

    if( m_xInput.is() )
    {
        try
        {
            m_xInput->closeInput();
        }
        catch( Exception &e )
        {
            // go down calm
        }
        m_xInput = Reference< XInputStream >();
    }
    if( m_xOutput.is() )
    {
        try
        {
            m_xOutput->closeOutput();
        }
        catch( Exception &e )
        {
            // go down calm
        }
        m_xOutput = Reference< XOutputStream >();
    }
    m_aListeners = list< Reference< XStreamListener > >();
    m_xSucc = Reference< XConnectable >();
    m_xPred = Reference< XConnectable >();
}

void Pump::static_run( void* pObject )
{
    ((Pump*)pObject)->run();
}

void Pump::run()
{
    Guard< Mutex > aGuard( m_aMutex );

    try
    {
        list< Reference< XStreamListener > >::iterator it;
        for( it = m_aListeners.begin(); it != m_aListeners.end(); ++it )
        {
            (*it)->started();
        }

        try
        {
            if( ! m_xInput.is() )
            {
                NotConnectedException exception(
                    OUString::createFromAscii( "no input stream set" ) , Reference<XInterface>((OWeakObject*)this) );
                throw exception;
            }
            Sequence< sal_Int8 > aData;
            long nBytes;
            while( nBytes = m_xInput->readSomeBytes( aData, 65536 ) )
            {
                if( ! m_xOutput.is() )
                {
                    NotConnectedException exception(
                        OUString::createFromAscii( "no output stream set" ) , Reference<XInterface>( (OWeakObject*)this) );
                    throw exception;
                }
                m_xOutput->writeBytes( aData );
                osl_yieldThread();
            }
        }
        catch ( IOException & e )
        {
            Any aException;
            aException <<= e;
            fireError( m_aListeners , aException );
        }
        catch ( RuntimeException & e )
        {
            Any aException;
            aException <<= e;
            fireError( m_aListeners , aException );
        }
        catch ( Exception & e )
        {
            Any aException;
            aException <<= e;
            fireError( m_aListeners , aException );
        }

        // listeners may remove themselves when called this way
        list< Reference< XStreamListener > > aLocalListeners = m_aListeners;
        close();

        for( it = aLocalListeners.begin();
             it != aLocalListeners.end(); ++it )
        {
            try
            {
                (*it)->closed();
            }
            catch ( RuntimeException &e )
            {
                OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
                OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners", sMessage.getStr() );
            }
        }
    }
    catch ( com::sun::star::uno::Exception &e )
    {
        // we are the last on the stack.
        // this is to avoid crashing the program, when e.g. a bridge crashes
        OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
        OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception", sMessage.getStr() );
    }

    release();
}

// ------------------------------------------------------------

/*
 * XConnectable
 */

void Pump::setPredecessor( const Reference< XConnectable >& xPred ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    m_xPred = xPred;
}

// ------------------------------------------------------------

Reference< XConnectable > Pump::getPredecessor() throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    return m_xPred;
}

// ------------------------------------------------------------

void Pump::setSuccessor( const Reference< XConnectable >& xSucc ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    m_xSucc = xSucc;
}

// ------------------------------------------------------------

Reference< XConnectable > Pump::getSuccessor() throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    return m_xSucc;
}

// -----------------------------------------------------------------

/*
 * XActiveDataControl
 */

void Pump::addListener( const Reference< XStreamListener >& xListener ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    m_aListeners.push_back( xListener );
}

// ------------------------------------------------------------

void Pump::removeListener( const Reference< XStreamListener >& xListener ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    m_aListeners.remove( xListener );
}

// ------------------------------------------------------------

void Pump::start() throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    acquire();
    m_aThread = osl_createThread(
        (oslWorkerFunction)Pump::static_run,
        (void*)this
        );
}

// ------------------------------------------------------------

void Pump::terminate() throw()
{
    // abort !
    close();

    // wait for the worker to die
    osl_joinWithThread( m_aThread );

    Guard< Mutex > aGuard( m_aMutex );

    // notify listeners
    list< Reference< XStreamListener > > aLocalListeners = m_aListeners;
    for( list< Reference< XStreamListener > >::iterator it = aLocalListeners.begin();
         it != aLocalListeners.end(); ++it )
    {
        try
        {
            (*it)->terminated();
        }
        catch( RuntimeException & e )
        {
            // simply ignore, maybe a bridge has crashed, notfiy other listeners as well
        }
    }
}

// ------------------------------------------------------------

/*
 * XActiveDataSink
 */

void Pump::setInputStream( const Reference< XInputStream >& xStream ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    m_xInput = xStream;
    Reference< XConnectable > xConnect( xStream, UNO_QUERY );
    if( xConnect.is() )
        xConnect->setSuccessor( this );
    // data transfer starts in XActiveDataControl::start
}

// ------------------------------------------------------------

Reference< XInputStream > Pump::getInputStream() throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    return m_xInput;
}

// ------------------------------------------------------------

/*
 * XActiveDataSource
 */

void Pump::setOutputStream( const Reference< XOutputStream >& xOut ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    m_xOutput = xOut;
    Reference< XConnectable > xConnect( xOut, UNO_QUERY );
    if( xConnect.is() )
        xConnect->setPredecessor( this );
    // data transfer starts in XActiveDataControl::start
}

// ------------------------------------------------------------

Reference< XOutputStream > Pump::getOutputStream() throw()
{
    Guard< Mutex > aGuard( m_aMutex );

    return m_xOutput;
}

// XServiceInfo
OUString Pump::getImplementationName() throw(  )
{
    return OPumpImpl_getImplementationName();
}

// XServiceInfo
sal_Bool Pump::supportsService(const OUString& ServiceName) throw(  )
{
    Sequence< OUString > aSNL = getSupportedServiceNames();
    const OUString * pArray = aSNL.getConstArray();

    for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
        if( pArray[i] == ServiceName )
            return sal_True;

    return sal_False;
}

// XServiceInfo
Sequence< OUString > Pump::getSupportedServiceNames(void) throw(  )
{
    return OPumpImpl_getSupportedServiceNames();
}

Reference< XInterface > SAL_CALL OPumpImpl_CreateInstance( const Reference< XComponentContext > & rSMgr ) throw (Exception)
{
    return Reference< XInterface >( *new Pump );
}

OUString OPumpImpl_getImplementationName()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.io.Pump") );
}

Sequence<OUString> OPumpImpl_getSupportedServiceNames(void)
{
    OUString s( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.Pump" ) );
    Sequence< OUString > seq( &s , 1 );
    return seq;
}

}

<commit_msg>#98224# fixed various concurrency and listener notification problems<commit_after>/*************************************************************************
 *
 *  $RCSfile: opump.cxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: jbu $ $Date: 2002-07-09 15:11:53 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>

#include <osl/diagnose.h>

#include <com/sun/star/io/XActiveDataSource.hpp>
#include <com/sun/star/io/XActiveDataSink.hpp>
#include <com/sun/star/io/XActiveDataControl.hpp>
#include <com/sun/star/io/XConnectable.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>

#include <uno/dispatcher.h>
#include <uno/mapping.hxx>
#include <cppuhelper/implbase5.hxx>
#include <cppuhelper/factory.hxx>
#include <cppuhelper/interfacecontainer.hxx>
#include <osl/mutex.hxx>
#include <osl/thread.h>

using namespace osl;
using namespace std;
using namespace rtl;
using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::registry;
using namespace com::sun::star::io;

#include "factreg.hxx"

namespace io_stm {

    class Pump : public WeakImplHelper5<
          XActiveDataSource, XActiveDataSink, XActiveDataControl, XConnectable, XServiceInfo >
    {
        Mutex                                   m_aMutex;
        oslThread                               m_aThread;

        Reference< XConnectable >               m_xPred;
        Reference< XConnectable >               m_xSucc;
        Reference< XInputStream >               m_xInput;
        Reference< XOutputStream >              m_xOutput;
        OInterfaceContainerHelper               m_cnt;
        sal_Bool                                m_closeFired;

        void run();
        static void static_run( void* pObject );

        void close();
        void fireClose();
        void fireStarted();
        void fireTerminated();
        void fireError( const Any &a );

    public:
        Pump();
        virtual ~Pump();

        // XActiveDataSource
        virtual void SAL_CALL setOutputStream( const Reference< ::com::sun::star::io::XOutputStream >& xOutput ) throw();
        virtual Reference< ::com::sun::star::io::XOutputStream > SAL_CALL getOutputStream() throw();

        // XActiveDataSink
        virtual void SAL_CALL setInputStream( const Reference< ::com::sun::star::io::XInputStream >& xStream ) throw();
        virtual Reference< ::com::sun::star::io::XInputStream > SAL_CALL getInputStream() throw();

        // XActiveDataControl
        virtual void SAL_CALL addListener( const Reference< ::com::sun::star::io::XStreamListener >& xListener ) throw();
        virtual void SAL_CALL removeListener( const Reference< ::com::sun::star::io::XStreamListener >& xListener ) throw();
        virtual void SAL_CALL start() throw( RuntimeException );
        virtual void SAL_CALL terminate() throw();

        // XConnectable
        virtual void SAL_CALL setPredecessor( const Reference< ::com::sun::star::io::XConnectable >& xPred ) throw();
        virtual Reference< ::com::sun::star::io::XConnectable > SAL_CALL getPredecessor() throw();
        virtual void SAL_CALL setSuccessor( const Reference< ::com::sun::star::io::XConnectable >& xSucc ) throw();
        virtual Reference< ::com::sun::star::io::XConnectable > SAL_CALL getSuccessor() throw();

    public: // XServiceInfo
        virtual OUString    SAL_CALL getImplementationName() throw(  );
        virtual Sequence< OUString > SAL_CALL getSupportedServiceNames(void) throw(  );
        virtual sal_Bool     SAL_CALL supportsService(const OUString& ServiceName) throw(  );
    };

Pump::Pump() : m_aThread( 0 ),
               m_cnt( m_aMutex ),
               m_closeFired( sal_False )
{
    g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
}

Pump::~Pump()
{
    // exit gracefully
    if( m_aThread )
    {
        osl_joinWithThread( m_aThread );
        osl_destroyThread( m_aThread );
    }
    g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}

void Pump::fireError( const  Any & exception )
{
    OInterfaceIteratorHelper iter( m_cnt );
    while( iter.hasMoreElements() )
    {
        try
        {
            static_cast< XStreamListener * > ( iter.next() )->error( exception );
        }
        catch ( RuntimeException &e )
        {
            OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
            OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners", sMessage.getStr() );
        }
    }
}

void Pump::fireClose()
{
    sal_Bool bFire = sal_False;
    {
        MutexGuard guard( m_aMutex );
        if( ! m_closeFired  )
        {
            m_closeFired = sal_True;
            bFire = sal_True;
        }
    }

    if( bFire )
    {
        OInterfaceIteratorHelper iter( m_cnt );
        while( iter.hasMoreElements() )
        {
            try
            {
                static_cast< XStreamListener * > ( iter.next() )->closed( );
            }
            catch ( RuntimeException &e )
            {
                OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
                OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners", sMessage.getStr() );
            }
        }
    }
}

void Pump::fireStarted()
{
    OInterfaceIteratorHelper iter( m_cnt );
    while( iter.hasMoreElements() )
    {
        try
        {
            static_cast< XStreamListener * > ( iter.next() )->started( );
        }
        catch ( RuntimeException &e )
        {
            OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
            OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners", sMessage.getStr() );
        }
    }
}

void Pump::fireTerminated()
{
    OInterfaceIteratorHelper iter( m_cnt );
    while( iter.hasMoreElements() )
    {
        try
        {
            static_cast< XStreamListener * > ( iter.next() )->terminated();
        }
        catch ( RuntimeException &e )
        {
            OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
            OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception during calling listeners", sMessage.getStr() );
        }
    }
}

void Pump::close()
{
    // close streams and release references
    Reference< XInputStream > rInput;
    Reference< XOutputStream > rOutput;
    {
        MutexGuard guard( m_aMutex );
        rInput = m_xInput;
        m_xInput.clear();

        rOutput = m_xOutput;
        m_xOutput.clear();
        m_xSucc.clear();
        m_xPred.clear();
    }
    if( rInput.is() )
    {
        try
        {
            rInput->closeInput();
        }
        catch( Exception &e )
        {
            // go down calm
        }
    }
    if( rOutput.is() )
    {
        try
        {
            rOutput->closeOutput();
        }
        catch( Exception &e )
        {
            // go down calm
        }
    }
}

void Pump::static_run( void* pObject )
{
    ((Pump*)pObject)->run();
    ((Pump*)pObject)->release();
}

void Pump::run()
{
    try
    {
        fireStarted();
        try
        {
            Reference< XInputStream > rInput;
            Reference< XOutputStream > rOutput;
            {
                Guard< Mutex > aGuard( m_aMutex );
                rInput = m_xInput;
                rOutput = m_xOutput;
            }

            if( ! rInput.is() )
            {
                NotConnectedException exception(
                    OUString::createFromAscii( "no input stream set" ) , Reference<XInterface>((OWeakObject*)this) );
                throw exception;
            }
            Sequence< sal_Int8 > aData;
            long nBytes;
            while( nBytes = rInput->readSomeBytes( aData, 65536 ) )
            {
                if( ! rOutput.is() )
                {
                    NotConnectedException exception(
                        OUString::createFromAscii( "no output stream set" ) , Reference<XInterface>( (OWeakObject*)this) );
                    throw exception;
                }
                rOutput->writeBytes( aData );
                osl_yieldThread();
            }
        }
        catch ( IOException & e )
        {
            fireError( makeAny( e ) );
        }
        catch ( RuntimeException & e )
        {
            fireError( makeAny( e ) );
        }
        catch ( Exception & e )
        {
            fireError( makeAny( e ) );
        }

        close();
        fireClose();
    }
    catch ( com::sun::star::uno::Exception &e )
    {
        // we are the last on the stack.
        // this is to avoid crashing the program, when e.g. a bridge crashes
        OString sMessage = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US );
        OSL_ENSURE( !"com.sun.star.comp.stoc.Pump: unexpected exception", sMessage.getStr() );
    }
}

// ------------------------------------------------------------

/*
 * XConnectable
 */

void Pump::setPredecessor( const Reference< XConnectable >& xPred ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    m_xPred = xPred;
}

// ------------------------------------------------------------

Reference< XConnectable > Pump::getPredecessor() throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    return m_xPred;
}

// ------------------------------------------------------------

void Pump::setSuccessor( const Reference< XConnectable >& xSucc ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    m_xSucc = xSucc;
}

// ------------------------------------------------------------

Reference< XConnectable > Pump::getSuccessor() throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    return m_xSucc;
}

// -----------------------------------------------------------------

/*
 * XActiveDataControl
 */

void Pump::addListener( const Reference< XStreamListener >& xListener ) throw()
{
    m_cnt.addInterface( xListener );
}

// ------------------------------------------------------------

void Pump::removeListener( const Reference< XStreamListener >& xListener ) throw()
{
    m_cnt.removeInterface( xListener );
}

// ------------------------------------------------------------

void Pump::start() throw( RuntimeException )
{
    Guard< Mutex > aGuard( m_aMutex );
    m_aThread = osl_createSuspendedThread((oslWorkerFunction)Pump::static_run,this);
    if( m_aThread )
    {
        // will be released by OPump::static_run
        acquire();
        osl_resumeThread( m_aThread );
    }
    else
    {
        throw RuntimeException(
            OUString( RTL_CONSTASCII_USTRINGPARAM( "Pump::start Couldn't create worker thread" )),
            *this);
    }
}

// ------------------------------------------------------------

void Pump::terminate() throw()
{
    close();

    // wait for the worker to die
    if( m_aThread )
        osl_joinWithThread( m_aThread );

    fireTerminated();
    fireClose();
}

// ------------------------------------------------------------

/*
 * XActiveDataSink
 */

void Pump::setInputStream( const Reference< XInputStream >& xStream ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    m_xInput = xStream;
    Reference< XConnectable > xConnect( xStream, UNO_QUERY );
    if( xConnect.is() )
        xConnect->setSuccessor( this );
    // data transfer starts in XActiveDataControl::start
}

// ------------------------------------------------------------

Reference< XInputStream > Pump::getInputStream() throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    return m_xInput;
}

// ------------------------------------------------------------

/*
 * XActiveDataSource
 */

void Pump::setOutputStream( const Reference< XOutputStream >& xOut ) throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    m_xOutput = xOut;
    Reference< XConnectable > xConnect( xOut, UNO_QUERY );
    if( xConnect.is() )
        xConnect->setPredecessor( this );
    // data transfer starts in XActiveDataControl::start
}

// ------------------------------------------------------------

Reference< XOutputStream > Pump::getOutputStream() throw()
{
    Guard< Mutex > aGuard( m_aMutex );
    return m_xOutput;
}

// XServiceInfo
OUString Pump::getImplementationName() throw(  )
{
    return OPumpImpl_getImplementationName();
}

// XServiceInfo
sal_Bool Pump::supportsService(const OUString& ServiceName) throw(  )
{
    Sequence< OUString > aSNL = getSupportedServiceNames();
    const OUString * pArray = aSNL.getConstArray();

    for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
        if( pArray[i] == ServiceName )
            return sal_True;

    return sal_False;
}

// XServiceInfo
Sequence< OUString > Pump::getSupportedServiceNames(void) throw(  )
{
    return OPumpImpl_getSupportedServiceNames();
}

Reference< XInterface > SAL_CALL OPumpImpl_CreateInstance( const Reference< XComponentContext > & rSMgr ) throw (Exception)
{
    return Reference< XInterface >( *new Pump );
}

OUString OPumpImpl_getImplementationName()
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.io.Pump") );
}

Sequence<OUString> OPumpImpl_getSupportedServiceNames(void)
{
    OUString s( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.Pump" ) );
    Sequence< OUString > seq( &s , 1 );
    return seq;
}

}

<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl>
 * Copyright (C) 2011 Shantanu Tushar <jhahoneyk@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "directoryprovider.h"

#include <QtGui/QDesktopServices>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>

using namespace GluonCore;

GLUON_DEFINE_SINGLETON( DirectoryProvider )

DirectoryProvider::DirectoryProvider( QObject* parent )
{
    m_userDataPath = QDesktopServices::storageLocation( QDesktopServices::DataLocation );
    m_userDataPath.remove( QCoreApplication::applicationName() );
    m_userDataPath.remove( QCoreApplication::organizationName() );
    m_userDataPath.append( "/gluon/" );

    //Define standard dirs Gluon recommends
    m_userDirs["data"] =  QDir::fromNativeSeparators( m_userDataPath + "/data" );
    m_userDirs["games"] = QDir::fromNativeSeparators( m_userDataPath + "/games" );

    //Create standard dirs Gluon recommends
    QDir dir;
    foreach( const QString& dirPath, m_userDirs )
    {
        dir.mkpath( dirPath );
    }
}

QString DirectoryProvider::installPrefix() const
{
    return GLUON_INSTALL_PREFIX;
}

QString DirectoryProvider::dataDirectory() const
{
    return GLUON_SHARE_INSTALL_DIR;
}

QString DirectoryProvider::libDirectory() const
{
    return GLUON_LIB_INSTALL_DIR;
}

QString DirectoryProvider::userDirectory( const QString& name )
{
    if( !m_userDirs.contains( name ) )
    {
        QString path = QDir::fromNativeSeparators( m_userDataPath + name );
        m_userDirs[name] = path;
        QDir dir;
        dir.mkpath( path );
    }

    return m_userDirs[name];
}

#include "directoryprovider.moc"
<commit_msg>Fix up the "no game is displayed" break in the player applications<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl>
 * Copyright (C) 2011 Shantanu Tushar <jhahoneyk@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "directoryprovider.h"

#include <QtGui/QDesktopServices>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>

using namespace GluonCore;

GLUON_DEFINE_SINGLETON( DirectoryProvider )

DirectoryProvider::DirectoryProvider( QObject* parent )
{
    m_userDataPath = QDesktopServices::storageLocation( QDesktopServices::DataLocation );
    m_userDataPath.remove( QCoreApplication::applicationName() );
    m_userDataPath.remove( QCoreApplication::organizationName() );
    m_userDataPath.append( "/gluon/" );

    //Define standard dirs Gluon recommends
    m_userDirs["data"] =  QDir::fromNativeSeparators( m_userDataPath + "/data" );
    m_userDirs["games"] = QDir::fromNativeSeparators( m_userDataPath + "/games" );

    //Create standard dirs Gluon recommends
    QDir dir;
    foreach( const QString& dirPath, m_userDirs )
    {
        dir.mkpath( dirPath );
    }
}

QString DirectoryProvider::installPrefix() const
{
    return GLUON_INSTALL_PREFIX;
}

QString DirectoryProvider::dataDirectory() const
{
    return GLUON_INSTALL_PREFIX + "/" + GLUON_SHARE_INSTALL_DIR;
}

QString DirectoryProvider::libDirectory() const
{
    return GLUON_INSTALL_PREFIX + "/" + GLUON_LIB_INSTALL_DIR;
}

QString DirectoryProvider::userDirectory( const QString& name )
{
    if( !m_userDirs.contains( name ) )
    {
        QString path = QDir::fromNativeSeparators( m_userDataPath + name );
        m_userDirs[name] = path;
        QDir dir;
        dir.mkpath( path );
    }

    return m_userDirs[name];
}

#include "directoryprovider.moc"
<|endoftext|>


<commit_before>/*
 * Copyright 2014 - 2015 Real Logic Ltd.
 *
 * Licensed 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef _SBE_MARKET_DATA_CODEC_BENCH_HPP
#define _SBE_MARKET_DATA_CODEC_BENCH_HPP

#include "CodecBench.hpp"
#include "uk_co_real_logic_sbe_perf_fix/MessageHeader.hpp"
#include "uk_co_real_logic_sbe_perf_fix/MarketDataIncrementalRefreshTrades.hpp"

using namespace uk_co_real_logic_sbe_perf_fix;

class SbeMarketDataCodecBench : public CodecBench<SbeMarketDataCodecBench>
{
public:
    virtual int encode(char *buffer, const int bufferLength)
    {
        messageHeader_.wrap(buffer, 0, 0, bufferLength);
        messageHeader_.blockLength(marketData_.sbeBlockLength());
        messageHeader_.templateId(marketData_.sbeTemplateId());
        messageHeader_.schemaId(marketData_.sbeSchemaId());
        messageHeader_.version(marketData_.sbeSchemaVersion());

        marketData_.wrapForEncode(buffer + messageHeader_.size(), 0, bufferLength);
        marketData_.transactTime(1234L);
        marketData_.eventTimeDelta(987);
        marketData_.matchEventIndicator(MatchEventIndicator::END_EVENT);

        MarketDataIncrementalRefreshTrades::MdIncGrp &mdIncGrp = marketData_.mdIncGrpCount(2);

        mdIncGrp.next();
        mdIncGrp.tradeId(1234L);
        mdIncGrp.securityId(56789L);
        mdIncGrp.mdEntryPx().mantissa(50);
        mdIncGrp.mdEntrySize().mantissa(10);
        mdIncGrp.numberOfOrders(1);
        mdIncGrp.mdUpdateAction(MDUpdateAction::NEW);
        mdIncGrp.rptSeq((short)1);
        mdIncGrp.aggressorSide(Side::BUY);

        mdIncGrp.next();
        mdIncGrp.tradeId(1234L);
        mdIncGrp.securityId(56789L);
        mdIncGrp.mdEntryPx().mantissa(50);
        mdIncGrp.mdEntrySize().mantissa(10);
        mdIncGrp.numberOfOrders(1);
        mdIncGrp.mdUpdateAction(MDUpdateAction::NEW);
        mdIncGrp.rptSeq((short)1);
        mdIncGrp.aggressorSide(Side::SELL);

        return MessageHeader::size() + marketData_.size();
    };

    virtual int decode(const char *buffer, const int bufferLength)
    {
        int64_t actingVersion;
        int64_t actingBlockLength;

        messageHeader_.wrap((char *)buffer, 0, 0, bufferLength);

        actingBlockLength = messageHeader_.blockLength();
        actingVersion = messageHeader_.version();

        marketData_.wrapForDecode((char *)buffer, messageHeader_.size(), actingBlockLength, actingVersion, bufferLength);

        marketData_.transactTime();
        marketData_.eventTimeDelta();
        marketData_.matchEventIndicator();

        MarketDataIncrementalRefreshTrades::MdIncGrp &mdIncGrp = marketData_.mdIncGrp();
        while (mdIncGrp.hasNext())
        {
            mdIncGrp.next();
            mdIncGrp.tradeId();
            mdIncGrp.securityId();
            mdIncGrp.mdEntryPx().mantissa();
            mdIncGrp.mdEntrySize().mantissa();
            mdIncGrp.numberOfOrders();
            mdIncGrp.mdUpdateAction();
            mdIncGrp.rptSeq();
            mdIncGrp.aggressorSide();
            mdIncGrp.mdEntryType();
        }

        return MessageHeader::size() + marketData_.size();
    };

private:
    MessageHeader messageHeader_;
    MarketDataIncrementalRefreshTrades marketData_;
};

#endif /* _SBE_MARKET_DATA_CODEC_BENCH_HPP */
<commit_msg>[c++]: Removed setting of constant enums in benchmarks.<commit_after>/*
 * Copyright 2014 - 2015 Real Logic Ltd.
 *
 * Licensed 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef _SBE_MARKET_DATA_CODEC_BENCH_HPP
#define _SBE_MARKET_DATA_CODEC_BENCH_HPP

#include "CodecBench.hpp"
#include "uk_co_real_logic_sbe_perf_fix/MessageHeader.hpp"
#include "uk_co_real_logic_sbe_perf_fix/MarketDataIncrementalRefreshTrades.hpp"

using namespace uk_co_real_logic_sbe_perf_fix;

class SbeMarketDataCodecBench : public CodecBench<SbeMarketDataCodecBench>
{
public:
    virtual int encode(char *buffer, const int bufferLength)
    {
        messageHeader_.wrap(buffer, 0, 0, bufferLength);
        messageHeader_.blockLength(marketData_.sbeBlockLength());
        messageHeader_.templateId(marketData_.sbeTemplateId());
        messageHeader_.schemaId(marketData_.sbeSchemaId());
        messageHeader_.version(marketData_.sbeSchemaVersion());

        marketData_.wrapForEncode(buffer + messageHeader_.size(), 0, bufferLength);
        marketData_.transactTime(1234L);
        marketData_.eventTimeDelta(987);
        marketData_.matchEventIndicator(MatchEventIndicator::END_EVENT);

        MarketDataIncrementalRefreshTrades::MdIncGrp &mdIncGrp = marketData_.mdIncGrpCount(2);

        mdIncGrp.next();
        mdIncGrp.tradeId(1234L);
        mdIncGrp.securityId(56789L);
        mdIncGrp.mdEntryPx().mantissa(50);
        mdIncGrp.mdEntrySize().mantissa(10);
        mdIncGrp.numberOfOrders(1);
        mdIncGrp.mdUpdateAction(MDUpdateAction::NEW);
        mdIncGrp.rptSeq((short)1);

        mdIncGrp.next();
        mdIncGrp.tradeId(1234L);
        mdIncGrp.securityId(56789L);
        mdIncGrp.mdEntryPx().mantissa(50);
        mdIncGrp.mdEntrySize().mantissa(10);
        mdIncGrp.numberOfOrders(1);
        mdIncGrp.mdUpdateAction(MDUpdateAction::NEW);
        mdIncGrp.rptSeq((short)1);

        return MessageHeader::size() + marketData_.size();
    };

    virtual int decode(const char *buffer, const int bufferLength)
    {
        int64_t actingVersion;
        int64_t actingBlockLength;

        messageHeader_.wrap((char *)buffer, 0, 0, bufferLength);

        actingBlockLength = messageHeader_.blockLength();
        actingVersion = messageHeader_.version();

        marketData_.wrapForDecode((char *)buffer, messageHeader_.size(), actingBlockLength, actingVersion, bufferLength);

        marketData_.transactTime();
        marketData_.eventTimeDelta();
        marketData_.matchEventIndicator();

        MarketDataIncrementalRefreshTrades::MdIncGrp &mdIncGrp = marketData_.mdIncGrp();
        while (mdIncGrp.hasNext())
        {
            mdIncGrp.next();
            mdIncGrp.tradeId();
            mdIncGrp.securityId();
            mdIncGrp.mdEntryPx().mantissa();
            mdIncGrp.mdEntrySize().mantissa();
            mdIncGrp.numberOfOrders();
            mdIncGrp.mdUpdateAction();
            mdIncGrp.rptSeq();
            mdIncGrp.aggressorSide();
            mdIncGrp.mdEntryType();
        }

        return MessageHeader::size() + marketData_.size();
    };

private:
    MessageHeader messageHeader_;
    MarketDataIncrementalRefreshTrades marketData_;
};

#endif /* _SBE_MARKET_DATA_CODEC_BENCH_HPP */
<|endoftext|>


<commit_before>/* Copyright (c) 2008-2017 the MRtrix3 contributors.
 *
 * 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/.
 *
 * MRtrix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include "header.h"
#include "phase_encoding.h"
#include "image_io/default.h"
#include "image_io/mosaic.h"
#include "file/dicom/mapper.h"
#include "file/dicom/image.h"
#include "file/dicom/series.h"
#include "file/dicom/study.h"
#include "file/dicom/patient.h"
#include "file/dicom/tree.h"

namespace MR {
  namespace File {
    namespace Dicom {

      std::unique_ptr<MR::ImageIO::Base> dicom_to_mapper (MR::Header& H, vector<std::shared_ptr<Series>>& series)
      {
        assert (series.size() > 0);
        std::unique_ptr<MR::ImageIO::Base> io_handler;

        Patient* patient (series[0]->study->patient);
        std::string sbuf = ( patient->name.size() ? patient->name : "unnamed" );
        sbuf += " " + format_ID (patient->ID);
        if (series[0]->modality.size())
          sbuf += std::string (" [") + series[0]->modality + "]";
        if (series[0]->name.size())
          sbuf += std::string (" ") + series[0]->name;
        add_line (H.keyval()["comments"], sbuf);
        H.name() = sbuf;

        // build up sorted list of frames:
        vector<Frame*> frames;

        // loop over series list:
        for (const auto series_it : series) {
          try {
            series_it->read();
          }
          catch (Exception& E) {
            E.display();
            throw Exception ("error reading series " + str (series_it->number) + " of DICOM image \"" + H.name() + "\"");
          }

          std::sort (series_it->begin(), series_it->end(), compare_ptr_contents());

          // loop over images in each series:
          for (auto image_it : *series_it) {
            if (!image_it->transfer_syntax_supported) {
              Exception E ("unsupported transfer syntax found in DICOM data");
              E.push_back ("consider using third-party tools to convert your data to standard uncompressed encoding");
              E.push_back ("See the MRtrix3 documentation on DICOM handling for details:");
              E.push_back ("   http://mrtrix.readthedocs.io/en/latest/tips_and_tricks/dicom_handling.html#error-unsupported-transfer-syntax");
              throw E;
            }
            // if multi-frame, loop over frames in image:
            if (image_it->frames.size()) {
              std::sort (image_it->frames.begin(), image_it->frames.end(), compare_ptr_contents());
              for (auto frame_it : image_it->frames)
                if (frame_it->image_type == series_it->image_type)
                  frames.push_back (frame_it.get());
            }
            // otherwise add image frame:
            else
              frames.push_back (image_it.get());
          }
        }

        auto dim = Frame::count (frames);

        if (dim[0]*dim[1]*dim[2] < frames.size())
          throw Exception ("dimensions mismatch in DICOM series");

        if (dim[0]*dim[1]*dim[2] > frames.size())
          throw Exception ("missing image frames for DICOM image \"" + H.name() + "\"");

        if (dim[0] > 1) { // switch axes so slice dim is inner-most:
          vector<Frame*> list (frames);
          vector<Frame*>::iterator it = frames.begin();
          for (size_t k = 0; k < dim[2]; ++k)
            for (size_t i = 0; i < dim[0]; ++i)
              for (size_t j = 0; j < dim[1]; ++j)
                *(it++) = list[i+dim[0]*(j+dim[1]*k)];
        }

        default_type slice_separation = Frame::get_slice_separation (frames, dim[1]);

        if (series[0]->study->name.size())
          add_line (H.keyval()["comments"], std::string ("study: " + series[0]->study->name + " [ " + series[0]->image_type + " ]"));

        if (patient->DOB.size())
          add_line (H.keyval()["comments"], std::string ("DOB: " + format_date (patient->DOB)));

        if (series[0]->date.size()) {
          sbuf = "DOS: " + format_date (series[0]->date);
          if (series[0]->time.size())
            sbuf += " " + format_time (series[0]->time);
          add_line (H.keyval()["comments"], sbuf);
        }

        const Image& image (*(*series[0])[0]);
        const Frame& frame (*frames[0]);

        if (std::isfinite (frame.echo_time))
          H.keyval()["EchoTime"] = str (0.001 * frame.echo_time, 6);

        size_t nchannels = image.frames.size() ? 1 : image.data_size / (frame.dim[0] * frame.dim[1] * (frame.bits_alloc/8));
        if (nchannels > 1)
          INFO ("data segment is larger than expected from image dimensions - interpreting as multi-channel data");

        H.ndim() = 3 + (dim[0]*dim[2]>1) + (nchannels>1);

        size_t current_axis = 0;

        if (nchannels > 1) {
          H.stride(3) = 1;
          H.size(3) = nchannels;
          ++current_axis;
        }

        H.stride(0) = ++current_axis;
        H.size(0) = frame.dim[0];
        H.spacing(0) = frame.pixel_size[0];

        H.stride(1) = ++current_axis;
        H.size(1) = frame.dim[1];
        H.spacing(1) = frame.pixel_size[1];

        H.stride(2) = ++current_axis;
        H.size(2) = dim[1];
        H.spacing(2) = slice_separation;

        if (dim[0]*dim[2] > 1) {
          H.stride(current_axis) = current_axis+1;
          H.size(current_axis) = dim[0]*dim[2];
          ++current_axis;
        }

        if (frame.bits_alloc == 8)
          H.datatype() = DataType::UInt8;
        else if (frame.bits_alloc == 16) {
          H.datatype() = DataType::UInt16;
          if (image.is_BE)
            H.datatype() = DataType::UInt16 | DataType::BigEndian;
          else
            H.datatype() = DataType::UInt16 | DataType::LittleEndian;
        }
        else throw Exception ("unexpected number of allocated bits per pixel (" + str (frame.bits_alloc)
            + ") in file \"" + H.name() + "\"");

        H.set_intensity_scaling (frame.scale_slope, frame.scale_intercept);

        // If multi-frame, take the transform information from the sorted frames; the first entry in the
        // vector should be the first slice of the first volume
        {
          transform_type M;

          M(0,0) = -frame.orientation_x[0];
          M(1,0) = -frame.orientation_x[1];
          M(2,0) = +frame.orientation_x[2];

          M(0,1) = -frame.orientation_y[0];
          M(1,1) = -frame.orientation_y[1];
          M(2,1) = +frame.orientation_y[2];

          M(0,2) = -frame.orientation_z[0];
          M(1,2) = -frame.orientation_z[1];
          M(2,2) = +frame.orientation_z[2];

          M(0,3) = -frame.position_vector[0];
          M(1,3) = -frame.position_vector[1];
          M(2,3) = +frame.position_vector[2];

          H.transform() = M;
          std::string dw_scheme = Frame::get_DW_scheme (frames, dim[1], M);
          if (dw_scheme.size())
            H.keyval()["dw_scheme"] = dw_scheme;
        }

        PhaseEncoding::set_scheme (H, Frame::get_PE_scheme (frames, dim[1]));

        for (size_t n = 1; n < frames.size(); ++n) // check consistency of data scaling:
          if (frames[n]->scale_intercept != frames[n-1]->scale_intercept ||
              frames[n]->scale_slope != frames[n-1]->scale_slope)
            throw Exception ("unable to load series due to inconsistent data scaling between DICOM images");

        if (image.images_in_mosaic) {
          INFO ("DICOM image \"" + H.name() + "\" is in mosaic format");
          if (H.size (2) != 1)
            throw Exception ("DICOM mosaic contains multiple slices in image \"" + H.name() + "\"");

          size_t mosaic_size = std::ceil (std::sqrt (image.images_in_mosaic));
          H.size(0) = std::floor (frame.dim[0] / mosaic_size);
          H.size(1) = std::floor (frame.dim[1] / mosaic_size);
          H.size(2) = image.images_in_mosaic;

          if (frame.acq_dim[0] != H.size(0)|| frame.acq_dim[1] != H.size(1)) {
            WARN ("acquisition matrix [ " + str (frame.acq_dim[0]) + " " + str (frame.acq_dim[1])
                + " ] does not fit into DICOM mosaic [ " + str (frame.dim[0]) + " " + str (frame.dim[1]) + " ]");
            WARN ("  data may have been zero-filled - matrix size has been adjusted to suit");
          }

          float xinc = H.spacing(0) * (frame.dim[0] - H.size(0)) / 2.0;
          float yinc = H.spacing(1) * (frame.dim[1] - H.size(1)) / 2.0;
          for (size_t i = 0; i < 3; i++)
            H.transform()(i,3) += xinc * H.transform()(i,0) + yinc * H.transform()(i,1);

          io_handler.reset (new MR::ImageIO::Mosaic (H, frame.dim[0], frame.dim[1], H.size (0), H.size (1), H.size (2)));
        }
        else
          io_handler.reset (new MR::ImageIO::Default (H));

        for (size_t n = 0; n < frames.size(); ++n)
          io_handler->files.push_back (File::Entry (frames[n]->filename, frames[n]->data));

        return io_handler;
      }

    }
  }
}

<commit_msg>DICOM: add and modify warnings related mosaic handling<commit_after>/* Copyright (c) 2008-2017 the MRtrix3 contributors.
 *
 * 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/.
 *
 * MRtrix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include "header.h"
#include "phase_encoding.h"
#include "image_io/default.h"
#include "image_io/mosaic.h"
#include "file/dicom/mapper.h"
#include "file/dicom/image.h"
#include "file/dicom/series.h"
#include "file/dicom/study.h"
#include "file/dicom/patient.h"
#include "file/dicom/tree.h"

namespace MR {
  namespace File {
    namespace Dicom {

      std::unique_ptr<MR::ImageIO::Base> dicom_to_mapper (MR::Header& H, vector<std::shared_ptr<Series>>& series)
      {
        assert (series.size() > 0);
        std::unique_ptr<MR::ImageIO::Base> io_handler;

        Patient* patient (series[0]->study->patient);
        std::string sbuf = ( patient->name.size() ? patient->name : "unnamed" );
        sbuf += " " + format_ID (patient->ID);
        if (series[0]->modality.size())
          sbuf += std::string (" [") + series[0]->modality + "]";
        if (series[0]->name.size())
          sbuf += std::string (" ") + series[0]->name;
        add_line (H.keyval()["comments"], sbuf);
        H.name() = sbuf;

        // build up sorted list of frames:
        vector<Frame*> frames;

        // loop over series list:
        for (const auto series_it : series) {
          try {
            series_it->read();
          }
          catch (Exception& E) {
            E.display();
            throw Exception ("error reading series " + str (series_it->number) + " of DICOM image \"" + H.name() + "\"");
          }

          std::sort (series_it->begin(), series_it->end(), compare_ptr_contents());

          // loop over images in each series:
          for (auto image_it : *series_it) {
            if (!image_it->transfer_syntax_supported) {
              Exception E ("unsupported transfer syntax found in DICOM data");
              E.push_back ("consider using third-party tools to convert your data to standard uncompressed encoding");
              E.push_back ("See the MRtrix3 documentation on DICOM handling for details:");
              E.push_back ("   http://mrtrix.readthedocs.io/en/latest/tips_and_tricks/dicom_handling.html#error-unsupported-transfer-syntax");
              throw E;
            }
            // if multi-frame, loop over frames in image:
            if (image_it->frames.size()) {
              std::sort (image_it->frames.begin(), image_it->frames.end(), compare_ptr_contents());
              for (auto frame_it : image_it->frames)
                if (frame_it->image_type == series_it->image_type)
                  frames.push_back (frame_it.get());
            }
            // otherwise add image frame:
            else
              frames.push_back (image_it.get());
          }
        }

        auto dim = Frame::count (frames);

        if (dim[0]*dim[1]*dim[2] < frames.size())
          throw Exception ("dimensions mismatch in DICOM series");

        if (dim[0]*dim[1]*dim[2] > frames.size())
          throw Exception ("missing image frames for DICOM image \"" + H.name() + "\"");

        if (dim[0] > 1) { // switch axes so slice dim is inner-most:
          vector<Frame*> list (frames);
          vector<Frame*>::iterator it = frames.begin();
          for (size_t k = 0; k < dim[2]; ++k)
            for (size_t i = 0; i < dim[0]; ++i)
              for (size_t j = 0; j < dim[1]; ++j)
                *(it++) = list[i+dim[0]*(j+dim[1]*k)];
        }

        default_type slice_separation = Frame::get_slice_separation (frames, dim[1]);

        if (series[0]->study->name.size())
          add_line (H.keyval()["comments"], std::string ("study: " + series[0]->study->name + " [ " + series[0]->image_type + " ]"));

        if (patient->DOB.size())
          add_line (H.keyval()["comments"], std::string ("DOB: " + format_date (patient->DOB)));

        if (series[0]->date.size()) {
          sbuf = "DOS: " + format_date (series[0]->date);
          if (series[0]->time.size())
            sbuf += " " + format_time (series[0]->time);
          add_line (H.keyval()["comments"], sbuf);
        }

        const Image& image (*(*series[0])[0]);
        const Frame& frame (*frames[0]);

        if (std::isfinite (frame.echo_time))
          H.keyval()["EchoTime"] = str (0.001 * frame.echo_time, 6);

        size_t nchannels = image.frames.size() ? 1 : image.data_size / (frame.dim[0] * frame.dim[1] * (frame.bits_alloc/8));
        if (nchannels > 1)
          INFO ("data segment is larger than expected from image dimensions - interpreting as multi-channel data");

        H.ndim() = 3 + (dim[0]*dim[2]>1) + (nchannels>1);

        size_t current_axis = 0;

        if (nchannels > 1) {
          H.stride(3) = 1;
          H.size(3) = nchannels;
          ++current_axis;
        }

        H.stride(0) = ++current_axis;
        H.size(0) = frame.dim[0];
        H.spacing(0) = frame.pixel_size[0];

        H.stride(1) = ++current_axis;
        H.size(1) = frame.dim[1];
        H.spacing(1) = frame.pixel_size[1];

        H.stride(2) = ++current_axis;
        H.size(2) = dim[1];
        H.spacing(2) = slice_separation;

        if (dim[0]*dim[2] > 1) {
          H.stride(current_axis) = current_axis+1;
          H.size(current_axis) = dim[0]*dim[2];
          ++current_axis;
        }

        if (frame.bits_alloc == 8)
          H.datatype() = DataType::UInt8;
        else if (frame.bits_alloc == 16) {
          H.datatype() = DataType::UInt16;
          if (image.is_BE)
            H.datatype() = DataType::UInt16 | DataType::BigEndian;
          else
            H.datatype() = DataType::UInt16 | DataType::LittleEndian;
        }
        else throw Exception ("unexpected number of allocated bits per pixel (" + str (frame.bits_alloc)
            + ") in file \"" + H.name() + "\"");

        H.set_intensity_scaling (frame.scale_slope, frame.scale_intercept);

        // If multi-frame, take the transform information from the sorted frames; the first entry in the
        // vector should be the first slice of the first volume
        {
          transform_type M;

          M(0,0) = -frame.orientation_x[0];
          M(1,0) = -frame.orientation_x[1];
          M(2,0) = +frame.orientation_x[2];

          M(0,1) = -frame.orientation_y[0];
          M(1,1) = -frame.orientation_y[1];
          M(2,1) = +frame.orientation_y[2];

          M(0,2) = -frame.orientation_z[0];
          M(1,2) = -frame.orientation_z[1];
          M(2,2) = +frame.orientation_z[2];

          M(0,3) = -frame.position_vector[0];
          M(1,3) = -frame.position_vector[1];
          M(2,3) = +frame.position_vector[2];

          H.transform() = M;
          std::string dw_scheme = Frame::get_DW_scheme (frames, dim[1], M);
          if (dw_scheme.size())
            H.keyval()["dw_scheme"] = dw_scheme;
        }

        PhaseEncoding::set_scheme (H, Frame::get_PE_scheme (frames, dim[1]));

        for (size_t n = 1; n < frames.size(); ++n) // check consistency of data scaling:
          if (frames[n]->scale_intercept != frames[n-1]->scale_intercept ||
              frames[n]->scale_slope != frames[n-1]->scale_slope)
            throw Exception ("unable to load series due to inconsistent data scaling between DICOM images");

        if (image.images_in_mosaic) {
          INFO ("DICOM image \"" + H.name() + "\" is in mosaic format");
          if (H.size (2) != 1)
            throw Exception ("DICOM mosaic contains multiple slices in image \"" + H.name() + "\"");

          size_t mosaic_size = std::ceil (std::sqrt (image.images_in_mosaic));
          H.size(0) = std::floor (frame.dim[0] / mosaic_size);
          H.size(1) = std::floor (frame.dim[1] / mosaic_size);
          H.size(2) = image.images_in_mosaic;

          if (frame.acq_dim[0] > H.size(0) || frame.acq_dim[1] > H.size(1)) {
            WARN ("acquisition matrix [ " + str (frame.acq_dim[0]) + " " + str (frame.acq_dim[1])
                + " ] is smaller than expected [ " + str(H.size(0)) + " " + str(H.size(1)) + " ] in DICOM mosaic");
            WARN ("  image may be incorrectly reformatted");
          }

          if (H.size(0)*mosaic_size != frame.dim[0] || H.size(1)*mosaic_size != frame.dim[1]) {
            WARN ("dimensions of DICOM mosaic [ " + str(frame.dim[0]) + " " + str(frame.dim[1])
                + " ] do not match expected size [ " + str(H.size(0)*mosaic_size) + " " + str(H.size(0)*mosaic_size) + " ]");
            WARN ("  assuming data are stored as " + str(mosaic_size)+"x"+str(mosaic_size) + " mosaic of " + str(H.size(0))+"x"+ str(H.size(1)) + " slices.");
            WARN ("  image may be incorrectly reformatted");
          }

          if (frame.acq_dim[0] != H.size(0)|| frame.acq_dim[1] != H.size(1))
            INFO ("note: acquisition matrix [ " + str (frame.acq_dim[0]) + " " + str (frame.acq_dim[1])
                + " ] differs from reconstructed matrix [ " + str(H.size(0)) + " " + str(H.size(1)) + " ]");

          float xinc = H.spacing(0) * (frame.dim[0] - H.size(0)) / 2.0;
          float yinc = H.spacing(1) * (frame.dim[1] - H.size(1)) / 2.0;
          for (size_t i = 0; i < 3; i++)
            H.transform()(i,3) += xinc * H.transform()(i,0) + yinc * H.transform()(i,1);

          io_handler.reset (new MR::ImageIO::Mosaic (H, frame.dim[0], frame.dim[1], H.size (0), H.size (1), H.size (2)));
        }
        else
          io_handler.reset (new MR::ImageIO::Default (H));

        for (size_t n = 0; n < frames.size(); ++n)
          io_handler->files.push_back (File::Entry (frames[n]->filename, frames[n]->data));

        return io_handler;
      }

    }
  }
}

<|endoftext|>


<commit_before>// Copyright Hugh Perkins 2016

// Licensed 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

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "function_dumper.h"

#include "type_dumper.h"
#include "GlobalNames.h"
#include "LocalNames.h"

#include "llvm/IRReader/IRReader.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"

#include <iostream>

#include "gtest/gtest.h"

using namespace std;
using namespace cocl;
using namespace llvm;

namespace test_function_dumper {

LLVMContext context;
unique_ptr<Module>M;

string ll_path = "../test/gtest/test_function_dumper.ll";  // this is a bit hacky, but fine-ish for now

Module *getM() {
    if(M == nullptr) {
        SMDiagnostic smDiagnostic;
        M = parseIRFile(StringRef(ll_path), smDiagnostic, context);
        if(!M) {
            smDiagnostic.print("irtopencl", errs());
            // return "";
            throw runtime_error("failed to parse IR");
            }
    }
    return M.get();
}

Function *getFunction(string name) {
    // Module *M = getM();
    getM();
    Function *F = M->getFunction(StringRef(name));
    if(F == 0) {
        throw runtime_error("Function " + name + " not found");
    }
    return F;
}

TEST(test_function_dumper, basic) {
    Function *F = getFunction("someKernel");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    // std::set< llvm::Function *> dumpedFunctions;
    map<Function *, Type *>returnTypeByFunction;
    functionDumper.runGeneration(returnTypeByFunction);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;

    for(auto it=functionDumper.neededFunctions.begin(); it != functionDumper.neededFunctions.end(); it++) {
        Function *childF = *it;
        cout << "needed function call: " << childF->getName().str() << endl;
    }
    ASSERT_EQ(1u, functionDumper.neededFunctions.size());
    ASSERT_EQ("someFunc_gp", (*functionDumper.neededFunctions.begin())->getName().str());
}

/*
TEST(test_function_dumper, usesShared1) {
    Function *F = getFunction("usesShared");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    std::set< llvm::Function *> dumpedFunctions;
    map<Function *, Type *>returnTypeByFunction;
    functionDumper.runGeneration(dumpedFunctions, returnTypeByFunction);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;

    ASSERT_EQ(1u, functionDumper.sharedVariablesToDeclare.size());
    string sharedDefinitions = functionDumper.dumpSharedDefinitions("    ");
    cout << "shareddefinitions: " << sharedDefinitions << endl;
    string expected_shared = "    local float mysharedmem[8];\n";
    ASSERT_EQ(expected_shared, sharedDefinitions);
}

TEST(test_function_dumper, usesShared2) {
    Function *F = getFunction("usesShared2");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    std::set< llvm::Function *> dumpedFunctions;
    map<Function *, Type *>returnTypeByFunction;
    functionDumper.runGeneration(dumpedFunctions, returnTypeByFunction);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;

    ASSERT_EQ(2u, functionDumper.sharedVariablesToDeclare.size());
    string sharedDefinitions = functionDumper.dumpSharedDefinitions("    ");
    cout << "shareddefinitions: " << sharedDefinitions << endl;
    string expected_shared = R"(    local float mysharedmem[8];
    local int anothershared[12];
)";
    ASSERT_EQ(expected_shared, sharedDefinitions);
}

TEST(test_function_dumper, usesPointerFunction) {
    Function *F = getFunction("usesPointerFunction");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    std::set< llvm::Function *> dumpedFunctions;
    map<Function *, Type *>returnTypeByFunction;
    bool genresult = functionDumper.runGeneration(dumpedFunctions, returnTypeByFunction);
    ASSERT_FALSE(genresult);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;

    Function *F2 = getFunction("returnsPointer");
    F2->dump();
    FunctionDumper functionDumper2(F2, true, &globalNames, &typeDumper, &functionNamesMap);
    bool genresult2 = functionDumper2.runGeneration(dumpedFunctions, returnTypeByFunction);
    ASSERT_TRUE(genresult2);
    ostringstream os2;
    functionDumper2.toCl(os2);
    string cl2 = os2.str();
    cout << "cl2:\n" << cl2 << endl;
}
*/
} // test_block_dumper
<commit_msg>test_function_dumper partly uncomneted now, need to put assertions back<commit_after>// Copyright Hugh Perkins 2016

// Licensed 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

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "function_dumper.h"

#include "type_dumper.h"
#include "GlobalNames.h"
#include "LocalNames.h"

#include "llvm/IRReader/IRReader.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"

#include <iostream>

#include "gtest/gtest.h"

using namespace std;
using namespace cocl;
using namespace llvm;

namespace test_function_dumper {

LLVMContext context;
unique_ptr<Module>M;

string ll_path = "../test/gtest/test_function_dumper.ll";  // this is a bit hacky, but fine-ish for now

Module *getM() {
    if(M == nullptr) {
        SMDiagnostic smDiagnostic;
        M = parseIRFile(StringRef(ll_path), smDiagnostic, context);
        if(!M) {
            smDiagnostic.print("irtopencl", errs());
            // return "";
            throw runtime_error("failed to parse IR");
            }
    }
    return M.get();
}

Function *getFunction(string name) {
    // Module *M = getM();
    getM();
    Function *F = M->getFunction(StringRef(name));
    if(F == 0) {
        throw runtime_error("Function " + name + " not found");
    }
    return F;
}

TEST(test_function_dumper, basic) {
    Function *F = getFunction("someKernel");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    // std::set< llvm::Function *> dumpedFunctions;
    map<Function *, Type *>returnTypeByFunction;
    functionDumper.runGeneration(returnTypeByFunction);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;

    for(auto it=functionDumper.neededFunctions.begin(); it != functionDumper.neededFunctions.end(); it++) {
        Function *childF = *it;
        cout << "needed function call: " << childF->getName().str() << endl;
    }
    ASSERT_EQ(1u, functionDumper.neededFunctions.size());
    ASSERT_EQ("someFunc_gp", (*functionDumper.neededFunctions.begin())->getName().str());
}

TEST(test_function_dumper, usesShared1) {
    Function *F = getFunction("usesShared");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    map<Function *, Type *>returnTypeByFunction;
    functionDumper.runGeneration(returnTypeByFunction);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;

    // ASSERT_EQ(1u, functionDumper.sharedVariablesToDeclare.size());
    // string sharedDefinitions = functionDumper.dumpSharedDefinitions("    ");
    // cout << "shareddefinitions: " << sharedDefinitions << endl;
    // string expected_shared = "    local float mysharedmem[8];\n";
    // ASSERT_EQ(expected_shared, sharedDefinitions);
}

TEST(test_function_dumper, usesShared2) {
    Function *F = getFunction("usesShared2");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    map<Function *, Type *>returnTypeByFunction;
    functionDumper.runGeneration(returnTypeByFunction);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;
/*
    ASSERT_EQ(2u, functionDumper.sharedVariablesToDeclare.size());
    string sharedDefinitions = functionDumper.dumpSharedDefinitions("    ");
    cout << "shareddefinitions: " << sharedDefinitions << endl;
    string expected_shared = R"(    local float mysharedmem[8];
    local int anothershared[12];
)";
    ASSERT_EQ(expected_shared, sharedDefinitions);
    */
}

TEST(test_function_dumper, usesPointerFunction) {
    Function *F = getFunction("usesPointerFunction");
    F->dump();
    // BasicBlock *block = &*F->begin();
    GlobalNames globalNames;
    LocalNames localNames;
    TypeDumper typeDumper(&globalNames);
    FunctionNamesMap functionNamesMap;
    FunctionDumper functionDumper(F, true, &globalNames, &typeDumper, &functionNamesMap);
    map<Function *, Type *>returnTypeByFunction;
    bool genresult = functionDumper.runGeneration(returnTypeByFunction);
    ASSERT_FALSE(genresult);
    ostringstream os;
    functionDumper.toCl(os);
    string cl = os.str();
    cout << "cl:\n" << cl << endl;
/*
    Function *F2 = getFunction("returnsPointer");
    F2->dump();
    FunctionDumper functionDumper2(F2, true, &globalNames, &typeDumper, &functionNamesMap);
    bool genresult2 = functionDumper2.runGeneration(dumpedFunctions, returnTypeByFunction);
    ASSERT_TRUE(genresult2);
    ostringstream os2;
    functionDumper2.toCl(os2);
    string cl2 = os2.str();
    cout << "cl2:\n" << cl2 << endl;
*/
}

} // test_block_dumper
<|endoftext|>


<commit_before>/*
    This file is part of solidity.

    solidity is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    solidity is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with solidity.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2016
 * Unit tests for inline assembly.
 */

#include "../TestHelper.h"

#include <libsolidity/inlineasm/AsmStack.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST.h>
#include <test/libsolidity/ErrorCheck.h>
#include <libevmasm/Assembly.h>

#include <boost/optional.hpp>

#include <string>
#include <memory>

using namespace std;

namespace dev
{
namespace solidity
{
namespace test
{

namespace
{

boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _assemble = false, bool _allowWarnings = true)
{
	assembly::InlineAssemblyStack stack;
	bool success = false;
	try
	{
		success = stack.parse(std::make_shared<Scanner>(CharStream(_source)));
		if (success && _assemble)
			stack.assemble();
	}
	catch (FatalError const&)
	{
		BOOST_FAIL("Fatal error leaked.");
		success = false;
	}
	if (!success)
	{
		BOOST_REQUIRE_EQUAL(stack.errors().size(), 1);
		return *stack.errors().front();
	}
	else
	{
		// If success is true, there might still be an error in the assembly stage.
		if (_allowWarnings && Error::containsOnlyWarnings(stack.errors()))
			return {};
		else if (!stack.errors().empty())
		{
			if (!_allowWarnings)
				BOOST_CHECK_EQUAL(stack.errors().size(), 1);
			return *stack.errors().front();
		}
	}
	return {};
}

bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true)
{
	return !parseAndReturnFirstError(_source, _assemble, _allowWarnings);
}

bool successAssemble(string const& _source, bool _allowWarnings = true)
{
	return successParse(_source, true, _allowWarnings);
}

Error expectError(std::string const& _source, bool _assemble, bool _allowWarnings = false)
{

	auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings);
	BOOST_REQUIRE(error);
	return *error;
}

void parsePrintCompare(string const& _source)
{
	assembly::InlineAssemblyStack stack;
	BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(_source))));
	BOOST_REQUIRE(stack.errors().empty());
	BOOST_CHECK_EQUAL(stack.toString(), _source);
}

}

#define CHECK_ERROR(text, assemble, typ, substring) \
do \
{ \
	Error err = expectError((text), (assemble), false); \
	BOOST_CHECK(err.type() == (Error::Type::typ)); \
	BOOST_CHECK(searchErrorMessage(err, (substring))); \
} while(0)

#define CHECK_PARSE_ERROR(text, type, substring) \
CHECK_ERROR(text, false, type, substring)

#define CHECK_ASSEMBLE_ERROR(text, type, substring) \
CHECK_ERROR(text, true, type, substring)

BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)

BOOST_AUTO_TEST_SUITE(Parsing)

BOOST_AUTO_TEST_CASE(smoke_test)
{
	BOOST_CHECK(successParse("{ }"));
}

BOOST_AUTO_TEST_CASE(simple_instructions)
{
	BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }"));
}

BOOST_AUTO_TEST_CASE(suicide_selfdestruct)
{
	BOOST_CHECK(successParse("{ 0x01 suicide 0x02 selfdestruct }"));
}

BOOST_AUTO_TEST_CASE(keywords)
{
	BOOST_CHECK(successParse("{ 1 2 byte 2 return address pop }"));
}

BOOST_AUTO_TEST_CASE(constants)
{
	BOOST_CHECK(successParse("{ 7 8 mul pop }"));
}

BOOST_AUTO_TEST_CASE(vardecl)
{
	BOOST_CHECK(successParse("{ let x := 7 }"));
}

BOOST_AUTO_TEST_CASE(assignment)
{
	BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }"));
}

BOOST_AUTO_TEST_CASE(label)
{
	BOOST_CHECK(successParse("{ 7 abc: 8 eq abc jump pop }"));
}

BOOST_AUTO_TEST_CASE(label_complex)
{
	BOOST_CHECK(successParse("{ 7 abc: 8 eq jump(abc) jumpi(eq(7, 8), abc) pop }"));
}

BOOST_AUTO_TEST_CASE(functional)
{
	BOOST_CHECK(successParse("{ let x := 2 add(7, mul(6, x)) mul(7, 8) add =: x }"));
}

BOOST_AUTO_TEST_CASE(functional_assignment)
{
	BOOST_CHECK(successParse("{ let x := 2 x := 7 }"));
}

BOOST_AUTO_TEST_CASE(functional_assignment_complex)
{
	BOOST_CHECK(successParse("{ let x := 2 x := add(7, mul(6, x)) mul(7, 8) add }"));
}

BOOST_AUTO_TEST_CASE(vardecl_complex)
{
	BOOST_CHECK(successParse("{ let y := 2 let x := add(7, mul(6, y)) add mul(7, 8) }"));
}

BOOST_AUTO_TEST_CASE(variable_use_before_decl)
{
	CHECK_PARSE_ERROR("{ x := 2 let x := 3 }", DeclarationError, "Variable x used before it was declared.");
	CHECK_PARSE_ERROR("{ let x := mul(2, x) }", DeclarationError, "Variable x used before it was declared.");
}

BOOST_AUTO_TEST_CASE(blocks)
{
	BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }"));
}

BOOST_AUTO_TEST_CASE(function_definitions)
{
	BOOST_CHECK(successParse("{ function f() { } function g(a) -> (x) { } }"));
}

BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
{
	BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> (x, y) { } }"));
}

BOOST_AUTO_TEST_CASE(function_calls)
{
	BOOST_CHECK(successParse("{ function f(a) -> (b) {} function g(a, b, c) {} function x() { g(1, 2, f(mul(2, 3))) x() } }"));
}

BOOST_AUTO_TEST_CASE(opcode_for_functions)
{
	CHECK_PARSE_ERROR("{ function gas() { } }", ParserError, "Cannot use instruction names for identifier names.");
}

BOOST_AUTO_TEST_CASE(opcode_for_function_args)
{
	CHECK_PARSE_ERROR("{ function f(gas) { } }", ParserError, "Cannot use instruction names for identifier names.");
	CHECK_PARSE_ERROR("{ function f() -> (gas) { } }", ParserError, "Cannot use instruction names for identifier names.");
}

BOOST_AUTO_TEST_CASE(name_clashes)
{
	CHECK_PARSE_ERROR("{ let g := 2 function g() { } }", DeclarationError, "Function name g already taken in this scope");
}

BOOST_AUTO_TEST_CASE(variable_access_cross_functions)
{
	CHECK_PARSE_ERROR("{ let x := 2 function g() { x pop } }", DeclarationError, "Identifier not found.");
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Printing)

BOOST_AUTO_TEST_CASE(print_smoke)
{
	parsePrintCompare("{\n}");
}

BOOST_AUTO_TEST_CASE(print_instructions)
{
	parsePrintCompare("{\n    7\n    8\n    mul\n    dup10\n    add\n    pop\n}");
}

BOOST_AUTO_TEST_CASE(print_subblock)
{
	parsePrintCompare("{\n    {\n        dup4\n        add\n    }\n}");
}

BOOST_AUTO_TEST_CASE(print_functional)
{
	parsePrintCompare("{\n    let x := mul(sload(0x12), 7)\n}");
}

BOOST_AUTO_TEST_CASE(print_label)
{
	parsePrintCompare("{\n    loop:\n    jump(loop)\n}");
}

BOOST_AUTO_TEST_CASE(print_assignments)
{
	parsePrintCompare("{\n    let x := mul(2, 3)\n    7\n    =: x\n    x := add(1, 2)\n}");
}

BOOST_AUTO_TEST_CASE(print_string_literals)
{
	parsePrintCompare("{\n    \"\\n'\\xab\\x95\\\"\"\n    pop\n}");
}

BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
{
	string source = "{ let x := \"\\u1bac\" }";
	string parsed = "{\n    let x := \"\\xe1\\xae\\xac\"\n}";
	assembly::InlineAssemblyStack stack;
	BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(source))));
	BOOST_REQUIRE(stack.errors().empty());
	BOOST_CHECK_EQUAL(stack.toString(), parsed);
	parsePrintCompare(parsed);
}

BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
{
	parsePrintCompare("{\n    function f(a, d)\n    {\n        mstore(a, d)\n    }\n    function g(a, d) -> (x, y)\n    {\n    }\n}");
}

BOOST_AUTO_TEST_CASE(function_calls)
{
	parsePrintCompare(
		"{\n    function y()\n    {\n    }\n    function f(a) -> (b)\n    {\n    }\n    function g(a, b, c)\n    {\n    }\n    g(1, mul(2, address), f(mul(2, caller)))\n    y()\n}"
	);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Analysis)

BOOST_AUTO_TEST_CASE(string_literals)
{
	BOOST_CHECK(successAssemble("{ let x := \"12345678901234567890123456789012\" }"));
}

BOOST_AUTO_TEST_CASE(oversize_string_literals)
{
	CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
}

BOOST_AUTO_TEST_CASE(assignment_after_tag)
{
	BOOST_CHECK(successParse("{ let x := 1 { 7 tag: =: x } }"));
}

BOOST_AUTO_TEST_CASE(magic_variables)
{
	CHECK_ASSEMBLE_ERROR("{ this pop }", DeclarationError, "Identifier not found");
	CHECK_ASSEMBLE_ERROR("{ ecrecover pop }", DeclarationError, "Identifier not found");
	BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover pop }"));
}

BOOST_AUTO_TEST_CASE(imbalanced_stack)
{
	BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
	CHECK_ASSEMBLE_ERROR("{ 1 }", DeclarationError, "Unbalanced stack at the end of a block: 1 surplus item(s).");
	CHECK_ASSEMBLE_ERROR("{ pop }", DeclarationError, "Unbalanced stack at the end of a block: 1 missing item(s).");
	BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
}

BOOST_AUTO_TEST_CASE(error_tag)
{
	BOOST_CHECK(successAssemble("{ jump(invalidJumpLabel) }"));
}

BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
{
	BOOST_CHECK(successAssemble("{ invalid }"));
}

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
{
	CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use instruction names for identifier names.");
}

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
{
	CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got instruction name.");
}

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
{
	CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Label name / variable name must precede \":\"");
}

BOOST_AUTO_TEST_CASE(revert)
{
	BOOST_CHECK(successAssemble("{ revert(0, 0) }"));
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()

}
}
} // end namespaces
<commit_msg>Use raw string for tests.<commit_after>/*
    This file is part of solidity.

    solidity is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    solidity is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with solidity.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2016
 * Unit tests for inline assembly.
 */

#include "../TestHelper.h"

#include <libsolidity/inlineasm/AsmStack.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST.h>
#include <test/libsolidity/ErrorCheck.h>
#include <libevmasm/Assembly.h>

#include <boost/optional.hpp>

#include <string>
#include <memory>

using namespace std;

namespace dev
{
namespace solidity
{
namespace test
{

namespace
{

boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _assemble = false, bool _allowWarnings = true)
{
	assembly::InlineAssemblyStack stack;
	bool success = false;
	try
	{
		success = stack.parse(std::make_shared<Scanner>(CharStream(_source)));
		if (success && _assemble)
			stack.assemble();
	}
	catch (FatalError const&)
	{
		BOOST_FAIL("Fatal error leaked.");
		success = false;
	}
	if (!success)
	{
		BOOST_REQUIRE_EQUAL(stack.errors().size(), 1);
		return *stack.errors().front();
	}
	else
	{
		// If success is true, there might still be an error in the assembly stage.
		if (_allowWarnings && Error::containsOnlyWarnings(stack.errors()))
			return {};
		else if (!stack.errors().empty())
		{
			if (!_allowWarnings)
				BOOST_CHECK_EQUAL(stack.errors().size(), 1);
			return *stack.errors().front();
		}
	}
	return {};
}

bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true)
{
	return !parseAndReturnFirstError(_source, _assemble, _allowWarnings);
}

bool successAssemble(string const& _source, bool _allowWarnings = true)
{
	return successParse(_source, true, _allowWarnings);
}

Error expectError(std::string const& _source, bool _assemble, bool _allowWarnings = false)
{

	auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings);
	BOOST_REQUIRE(error);
	return *error;
}

void parsePrintCompare(string const& _source)
{
	assembly::InlineAssemblyStack stack;
	BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(_source))));
	BOOST_REQUIRE(stack.errors().empty());
	BOOST_CHECK_EQUAL(stack.toString(), _source);
}

}

#define CHECK_ERROR(text, assemble, typ, substring) \
do \
{ \
	Error err = expectError((text), (assemble), false); \
	BOOST_CHECK(err.type() == (Error::Type::typ)); \
	BOOST_CHECK(searchErrorMessage(err, (substring))); \
} while(0)

#define CHECK_PARSE_ERROR(text, type, substring) \
CHECK_ERROR(text, false, type, substring)

#define CHECK_ASSEMBLE_ERROR(text, type, substring) \
CHECK_ERROR(text, true, type, substring)

BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly)

BOOST_AUTO_TEST_SUITE(Parsing)

BOOST_AUTO_TEST_CASE(smoke_test)
{
	BOOST_CHECK(successParse("{ }"));
}

BOOST_AUTO_TEST_CASE(simple_instructions)
{
	BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }"));
}

BOOST_AUTO_TEST_CASE(suicide_selfdestruct)
{
	BOOST_CHECK(successParse("{ 0x01 suicide 0x02 selfdestruct }"));
}

BOOST_AUTO_TEST_CASE(keywords)
{
	BOOST_CHECK(successParse("{ 1 2 byte 2 return address pop }"));
}

BOOST_AUTO_TEST_CASE(constants)
{
	BOOST_CHECK(successParse("{ 7 8 mul pop }"));
}

BOOST_AUTO_TEST_CASE(vardecl)
{
	BOOST_CHECK(successParse("{ let x := 7 }"));
}

BOOST_AUTO_TEST_CASE(assignment)
{
	BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }"));
}

BOOST_AUTO_TEST_CASE(label)
{
	BOOST_CHECK(successParse("{ 7 abc: 8 eq abc jump pop }"));
}

BOOST_AUTO_TEST_CASE(label_complex)
{
	BOOST_CHECK(successParse("{ 7 abc: 8 eq jump(abc) jumpi(eq(7, 8), abc) pop }"));
}

BOOST_AUTO_TEST_CASE(functional)
{
	BOOST_CHECK(successParse("{ let x := 2 add(7, mul(6, x)) mul(7, 8) add =: x }"));
}

BOOST_AUTO_TEST_CASE(functional_assignment)
{
	BOOST_CHECK(successParse("{ let x := 2 x := 7 }"));
}

BOOST_AUTO_TEST_CASE(functional_assignment_complex)
{
	BOOST_CHECK(successParse("{ let x := 2 x := add(7, mul(6, x)) mul(7, 8) add }"));
}

BOOST_AUTO_TEST_CASE(vardecl_complex)
{
	BOOST_CHECK(successParse("{ let y := 2 let x := add(7, mul(6, y)) add mul(7, 8) }"));
}

BOOST_AUTO_TEST_CASE(variable_use_before_decl)
{
	CHECK_PARSE_ERROR("{ x := 2 let x := 3 }", DeclarationError, "Variable x used before it was declared.");
	CHECK_PARSE_ERROR("{ let x := mul(2, x) }", DeclarationError, "Variable x used before it was declared.");
}

BOOST_AUTO_TEST_CASE(blocks)
{
	BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }"));
}

BOOST_AUTO_TEST_CASE(function_definitions)
{
	BOOST_CHECK(successParse("{ function f() { } function g(a) -> (x) { } }"));
}

BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
{
	BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> (x, y) { } }"));
}

BOOST_AUTO_TEST_CASE(function_calls)
{
	BOOST_CHECK(successParse("{ function f(a) -> (b) {} function g(a, b, c) {} function x() { g(1, 2, f(mul(2, 3))) x() } }"));
}

BOOST_AUTO_TEST_CASE(opcode_for_functions)
{
	CHECK_PARSE_ERROR("{ function gas() { } }", ParserError, "Cannot use instruction names for identifier names.");
}

BOOST_AUTO_TEST_CASE(opcode_for_function_args)
{
	CHECK_PARSE_ERROR("{ function f(gas) { } }", ParserError, "Cannot use instruction names for identifier names.");
	CHECK_PARSE_ERROR("{ function f() -> (gas) { } }", ParserError, "Cannot use instruction names for identifier names.");
}

BOOST_AUTO_TEST_CASE(name_clashes)
{
	CHECK_PARSE_ERROR("{ let g := 2 function g() { } }", DeclarationError, "Function name g already taken in this scope");
}

BOOST_AUTO_TEST_CASE(variable_access_cross_functions)
{
	CHECK_PARSE_ERROR("{ let x := 2 function g() { x pop } }", DeclarationError, "Identifier not found.");
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Printing)

BOOST_AUTO_TEST_CASE(print_smoke)
{
	parsePrintCompare("{\n}");
}

BOOST_AUTO_TEST_CASE(print_instructions)
{
	parsePrintCompare("{\n    7\n    8\n    mul\n    dup10\n    add\n    pop\n}");
}

BOOST_AUTO_TEST_CASE(print_subblock)
{
	parsePrintCompare("{\n    {\n        dup4\n        add\n    }\n}");
}

BOOST_AUTO_TEST_CASE(print_functional)
{
	parsePrintCompare("{\n    let x := mul(sload(0x12), 7)\n}");
}

BOOST_AUTO_TEST_CASE(print_label)
{
	parsePrintCompare("{\n    loop:\n    jump(loop)\n}");
}

BOOST_AUTO_TEST_CASE(print_assignments)
{
	parsePrintCompare("{\n    let x := mul(2, 3)\n    7\n    =: x\n    x := add(1, 2)\n}");
}

BOOST_AUTO_TEST_CASE(print_string_literals)
{
	parsePrintCompare("{\n    \"\\n'\\xab\\x95\\\"\"\n    pop\n}");
}

BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
{
	string source = "{ let x := \"\\u1bac\" }";
	string parsed = "{\n    let x := \"\\xe1\\xae\\xac\"\n}";
	assembly::InlineAssemblyStack stack;
	BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(source))));
	BOOST_REQUIRE(stack.errors().empty());
	BOOST_CHECK_EQUAL(stack.toString(), parsed);
	parsePrintCompare(parsed);
}

BOOST_AUTO_TEST_CASE(function_definitions_multiple_args)
{
	parsePrintCompare("{\n    function f(a, d)\n    {\n        mstore(a, d)\n    }\n    function g(a, d) -> (x, y)\n    {\n    }\n}");
}

BOOST_AUTO_TEST_CASE(function_calls)
{
	string source = R"({
	function y()
	{
	}
	function f(a) -> (b)
	{
	}
	function g(a, b, c)
	{
	}
	g(1, mul(2, address), f(mul(2, caller)))
	y()
})";
	boost::replace_all(source, "\t", "    ");
	parsePrintCompare(source);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Analysis)

BOOST_AUTO_TEST_CASE(string_literals)
{
	BOOST_CHECK(successAssemble("{ let x := \"12345678901234567890123456789012\" }"));
}

BOOST_AUTO_TEST_CASE(oversize_string_literals)
{
	CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
}

BOOST_AUTO_TEST_CASE(assignment_after_tag)
{
	BOOST_CHECK(successParse("{ let x := 1 { 7 tag: =: x } }"));
}

BOOST_AUTO_TEST_CASE(magic_variables)
{
	CHECK_ASSEMBLE_ERROR("{ this pop }", DeclarationError, "Identifier not found");
	CHECK_ASSEMBLE_ERROR("{ ecrecover pop }", DeclarationError, "Identifier not found");
	BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover pop }"));
}

BOOST_AUTO_TEST_CASE(imbalanced_stack)
{
	BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
	CHECK_ASSEMBLE_ERROR("{ 1 }", DeclarationError, "Unbalanced stack at the end of a block: 1 surplus item(s).");
	CHECK_ASSEMBLE_ERROR("{ pop }", DeclarationError, "Unbalanced stack at the end of a block: 1 missing item(s).");
	BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
}

BOOST_AUTO_TEST_CASE(error_tag)
{
	BOOST_CHECK(successAssemble("{ jump(invalidJumpLabel) }"));
}

BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
{
	BOOST_CHECK(successAssemble("{ invalid }"));
}

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
{
	CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use instruction names for identifier names.");
}

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
{
	CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got instruction name.");
}

BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
{
	CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Label name / variable name must precede \":\"");
}

BOOST_AUTO_TEST_CASE(revert)
{
	BOOST_CHECK(successAssemble("{ revert(0, 0) }"));
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()

}
}
} // end namespaces
<|endoftext|>