/* RPC call and callback templates
   Copyright (C) 2014-2017 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3.  If not see
.  */
#ifndef CC1_PLUGIN_RPC_HH
#define CC1_PLUGIN_RPC_HH
#include "status.hh"
#include "connection.hh"
namespace cc1_plugin
{
  // The plugin API may contain some "const" method parameters.
  // However, when unmarshalling we cannot unmarshall into a const
  // object; and furthermore we want to be able to deallocate pointers
  // when finished with them.  This wrapper class lets us properly
  // remove the "const" and handle deallocation from pointer types.
  template
  class argument_wrapper
  {
  public:
    argument_wrapper () { }
    ~argument_wrapper () { }
    operator T () const { return m_object; }
    status unmarshall (connection *conn)
    {
      return ::cc1_plugin::unmarshall (conn, &m_object);
    }
  private:
    T m_object;
    // No copying or assignment allowed.
    argument_wrapper (const argument_wrapper &);
    argument_wrapper &operator= (const argument_wrapper &);
  };
  // Specialization for any kind of pointer.  This is declared but not
  // defined to avoid bugs if a new pointer type is introduced into
  // the API.  Instead you will just get a compilation error.
  template
  class argument_wrapper;
  // Specialization for string types.
  template<>
  class argument_wrapper
  {
  public:
    argument_wrapper () : m_object (NULL) { }
    ~argument_wrapper ()
    {
      delete[] m_object;
    }
    operator const char * () const
    {
      return m_object;
    }
    status unmarshall (connection *conn)
    {
      return ::cc1_plugin::unmarshall (conn, &m_object);
    }
  private:
    char *m_object;
    // No copying or assignment allowed.
    argument_wrapper (const argument_wrapper &);
    argument_wrapper &operator= (const argument_wrapper &);
  };
  // Specialization for gcc_type_array.
  template<>
  class argument_wrapper
  {
  public:
    argument_wrapper () : m_object (NULL) { }
    ~argument_wrapper ()
    {
      // It would be nicer if gcc_type_array could have a destructor.
      // But, it is in code shared with gdb and cannot.
      if (m_object != NULL)
	delete[] m_object->elements;
      delete m_object;
    }
    operator const gcc_type_array * () const
    {
      return m_object;
    }
    status unmarshall (connection *conn)
    {
      return ::cc1_plugin::unmarshall (conn, &m_object);
    }
  private:
    gcc_type_array *m_object;
    // No copying or assignment allowed.
    argument_wrapper (const argument_wrapper &);
    argument_wrapper &operator= (const argument_wrapper &);
  };
#ifdef GCC_CP_INTERFACE_H
  // Specialization for gcc_vbase_array.
  template<>
  class argument_wrapper
  {
  public:
    argument_wrapper () : m_object (NULL) { }
    ~argument_wrapper ()
    {
      // It would be nicer if gcc_type_array could have a destructor.
      // But, it is in code shared with gdb and cannot.
      if (m_object != NULL)
	{
	  delete[] m_object->flags;
	  delete[] m_object->elements;
	}
      delete m_object;
    }
    operator const gcc_vbase_array * () const
    {
      return m_object;
    }
    status unmarshall (connection *conn)
    {
      return ::cc1_plugin::unmarshall (conn, &m_object);
    }
  private:
    gcc_vbase_array *m_object;
    // No copying or assignment allowed.
    argument_wrapper (const argument_wrapper &);
    argument_wrapper &operator= (const argument_wrapper &);
  };
  // Specialization for gcc_cp_template_args.
  template<>
  class argument_wrapper
  {
  public:
    argument_wrapper () : m_object (NULL) { }
    ~argument_wrapper ()
    {
      // It would be nicer if gcc_type_array could have a destructor.
      // But, it is in code shared with gdb and cannot.
      if (m_object != NULL)
	{
	  delete[] m_object->elements;
	  delete[] m_object->kinds;
	}
      delete m_object;
    }
    operator const gcc_cp_template_args * () const
    {
      return m_object;
    }
    status unmarshall (connection *conn)
    {
      return ::cc1_plugin::unmarshall (conn, &m_object);
    }
  private:
    gcc_cp_template_args *m_object;
    // No copying or assignment allowed.
    argument_wrapper (const argument_wrapper &);
    argument_wrapper &operator= (const argument_wrapper &);
  };
  // Specialization for gcc_cp_function_args.
  template<>
  class argument_wrapper
  {
  public:
    argument_wrapper () : m_object (NULL) { }
    ~argument_wrapper ()
    {
      // It would be nicer if gcc_type_array could have a destructor.
      // But, it is in code shared with gdb and cannot.
      if (m_object != NULL)
	{
	  delete[] m_object->elements;
	}
      delete m_object;
    }
    operator const gcc_cp_function_args * () const
    {
      return m_object;
    }
    status unmarshall (connection *conn)
    {
      return ::cc1_plugin::unmarshall (conn, &m_object);
    }
  private:
    gcc_cp_function_args *m_object;
    // No copying or assignment allowed.
    argument_wrapper (const argument_wrapper &);
    argument_wrapper &operator= (const argument_wrapper &);
  };
#endif /* GCC_CP_INTERFACE_H */
  // There are two kinds of template functions here: "call" and
  // "callback".  They are each repeated multiple times to handle
  // different numbers of arguments.  (This would be improved with
  // C++11, though applying a call is still tricky until C++14 can be
  // used.)
  // The "call" template is used for making a remote procedure call.
  // It starts a query ('Q') packet, marshalls its arguments, waits
  // for a result, and finally reads and returns the result via an
  // "out" parameter.
  // The "callback" template is used when receiving a remote procedure
  // call.  This template function is suitable for use with the
  // "callbacks" and "connection" classes.  It decodes incoming
  // arguments, passes them to the wrapped function, and finally
  // marshalls a reply packet.
  template
  status
  call (connection *conn, const char *method, R *result)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 0))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    R result;
    if (!unmarshall_check (conn, 0))
      return FAIL;
    result = func (conn);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
  template
  status
  call (connection *conn, const char *method, R *result, A arg)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 1))
      return FAIL;
    if (!marshall (conn, arg))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    argument_wrapper arg;
    R result;
    if (!unmarshall_check (conn, 1))
      return FAIL;
    if (!arg.unmarshall (conn))
      return FAIL;
    result = func (conn, arg);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
  template
  status
  call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 2))
      return FAIL;
    if (!marshall (conn, arg1))
      return FAIL;
    if (!marshall (conn, arg2))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    argument_wrapper arg1;
    argument_wrapper arg2;
    R result;
    if (!unmarshall_check (conn, 2))
      return FAIL;
    if (!arg1.unmarshall (conn))
      return FAIL;
    if (!arg2.unmarshall (conn))
      return FAIL;
    result = func (conn, arg1, arg2);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
  template
  status
  call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2,
	A3 arg3)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 3))
      return FAIL;
    if (!marshall (conn, arg1))
      return FAIL;
    if (!marshall (conn, arg2))
      return FAIL;
    if (!marshall (conn, arg3))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    argument_wrapper arg1;
    argument_wrapper arg2;
    argument_wrapper arg3;
    R result;
    if (!unmarshall_check (conn, 3))
      return FAIL;
    if (!arg1.unmarshall (conn))
      return FAIL;
    if (!arg2.unmarshall (conn))
      return FAIL;
    if (!arg3.unmarshall (conn))
      return FAIL;
    result = func (conn, arg1, arg2, arg3);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
  template
  status
  call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2,
	A3 arg3, A4 arg4)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 4))
      return FAIL;
    if (!marshall (conn, arg1))
      return FAIL;
    if (!marshall (conn, arg2))
      return FAIL;
    if (!marshall (conn, arg3))
      return FAIL;
    if (!marshall (conn, arg4))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    argument_wrapper arg1;
    argument_wrapper arg2;
    argument_wrapper arg3;
    argument_wrapper arg4;
    R result;
    if (!unmarshall_check (conn, 4))
      return FAIL;
    if (!arg1.unmarshall (conn))
      return FAIL;
    if (!arg2.unmarshall (conn))
      return FAIL;
    if (!arg3.unmarshall (conn))
      return FAIL;
    if (!arg4.unmarshall (conn))
      return FAIL;
    result = func (conn, arg1, arg2, arg3, arg4);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
  template
  status
  call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2,
	A3 arg3, A4 arg4, A5 arg5)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 5))
      return FAIL;
    if (!marshall (conn, arg1))
      return FAIL;
    if (!marshall (conn, arg2))
      return FAIL;
    if (!marshall (conn, arg3))
      return FAIL;
    if (!marshall (conn, arg4))
      return FAIL;
    if (!marshall (conn, arg5))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    argument_wrapper arg1;
    argument_wrapper arg2;
    argument_wrapper arg3;
    argument_wrapper arg4;
    argument_wrapper arg5;
    R result;
    if (!unmarshall_check (conn, 5))
      return FAIL;
    if (!arg1.unmarshall (conn))
      return FAIL;
    if (!arg2.unmarshall (conn))
      return FAIL;
    if (!arg3.unmarshall (conn))
      return FAIL;
    if (!arg4.unmarshall (conn))
      return FAIL;
    if (!arg5.unmarshall (conn))
      return FAIL;
    result = func (conn, arg1, arg2, arg3, arg4, arg5);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
  template
  status
  call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2,
	A3 arg3, A4 arg4, A5 arg5, A6 arg6, A7 arg7)
  {
    if (!conn->send ('Q'))
      return FAIL;
    if (!marshall (conn, method))
      return FAIL;
    if (!marshall (conn, 7))
      return FAIL;
    if (!marshall (conn, arg1))
      return FAIL;
    if (!marshall (conn, arg2))
      return FAIL;
    if (!marshall (conn, arg3))
      return FAIL;
    if (!marshall (conn, arg4))
      return FAIL;
    if (!marshall (conn, arg5))
      return FAIL;
    if (!marshall (conn, arg6))
      return FAIL;
    if (!marshall (conn, arg7))
      return FAIL;
    if (!conn->wait_for_result ())
      return FAIL;
    if (!unmarshall (conn, result))
      return FAIL;
    return OK;
  }
  template
  status
  callback (connection *conn)
  {
    argument_wrapper arg1;
    argument_wrapper arg2;
    argument_wrapper arg3;
    argument_wrapper arg4;
    argument_wrapper arg5;
    argument_wrapper arg6;
    argument_wrapper arg7;
    R result;
    if (!unmarshall_check (conn, 7))
      return FAIL;
    if (!arg1.unmarshall (conn))
      return FAIL;
    if (!arg2.unmarshall (conn))
      return FAIL;
    if (!arg3.unmarshall (conn))
      return FAIL;
    if (!arg4.unmarshall (conn))
      return FAIL;
    if (!arg5.unmarshall (conn))
      return FAIL;
    if (!arg6.unmarshall (conn))
      return FAIL;
    if (!arg7.unmarshall (conn))
      return FAIL;
    result = func (conn, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
    if (!conn->send ('R'))
      return FAIL;
    return marshall (conn, result);
  }
};
#endif // CC1_PLUGIN_RPC_HH