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