//===- llvm/ExecutionEngine/Orc/RPC/RPCSerialization.h --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H #define LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H #include "llvm/ExecutionEngine/Orc/OrcError.h" #include "llvm/Support/thread.h" #include #include #include #include #include #include namespace llvm { namespace orc { namespace rpc { template class RPCTypeName; /// TypeNameSequence is a utility for rendering sequences of types to a string /// by rendering each type, separated by ", ". template class RPCTypeNameSequence {}; /// Render an empty TypeNameSequence to an ostream. template OStream &operator<<(OStream &OS, const RPCTypeNameSequence<> &V) { return OS; } /// Render a TypeNameSequence of a single type to an ostream. template OStream &operator<<(OStream &OS, const RPCTypeNameSequence &V) { OS << RPCTypeName::getName(); return OS; } /// Render a TypeNameSequence of more than one type to an ostream. template OStream& operator<<(OStream &OS, const RPCTypeNameSequence &V) { OS << RPCTypeName::getName() << ", " << RPCTypeNameSequence(); return OS; } template <> class RPCTypeName { public: static const char* getName() { return "void"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int8_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint8_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int16_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint16_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int32_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint32_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "int64_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "uint64_t"; } }; template <> class RPCTypeName { public: static const char* getName() { return "bool"; } }; template <> class RPCTypeName { public: static const char* getName() { return "std::string"; } }; template <> class RPCTypeName { public: static const char* getName() { return "Error"; } }; template class RPCTypeName> { public: static const char* getName() { static std::string Name = [] { std::string Name; raw_string_ostream(Name) << "Expected<" << RPCTypeNameSequence() << ">"; return Name; }(); return Name.data(); } }; template class RPCTypeName> { public: static const char* getName() { static std::string Name = [] { std::string Name; raw_string_ostream(Name) << "std::pair<" << RPCTypeNameSequence() << ">"; return Name; }(); return Name.data(); } }; template class RPCTypeName> { public: static const char* getName() { static std::string Name = [] { std::string Name; raw_string_ostream(Name) << "std::tuple<" << RPCTypeNameSequence() << ">"; return Name; }(); return Name.data(); } }; template class RPCTypeName> { public: static const char*getName() { static std::string Name = [] { std::string Name; raw_string_ostream(Name) << "std::vector<" << RPCTypeName::getName() << ">"; return Name; }(); return Name.data(); } }; template class RPCTypeName> { public: static const char *getName() { static std::string Name = [] { std::string Name; raw_string_ostream(Name) << "std::set<" << RPCTypeName::getName() << ">"; return Name; }(); return Name.data(); } }; template class RPCTypeName> { public: static const char *getName() { static std::string Name = [] { std::string Name; raw_string_ostream(Name) << "std::map<" << RPCTypeNameSequence() << ">"; return Name; }(); return Name.data(); } }; /// The SerializationTraits class describes how to serialize and /// deserialize an instance of type T to/from an abstract channel of type /// ChannelT. It also provides a representation of the type's name via the /// getName method. /// /// Specializations of this class should provide the following functions: /// /// @code{.cpp} /// /// static const char* getName(); /// static Error serialize(ChannelT&, const T&); /// static Error deserialize(ChannelT&, T&); /// /// @endcode /// /// The third argument of SerializationTraits is intended to support SFINAE. /// E.g.: /// /// @code{.cpp} /// /// class MyVirtualChannel { ... }; /// /// template /// class SerializationTraits::value /// >::type> { /// public: /// static const char* getName() { ... }; /// } /// /// @endcode template class SerializationTraits; template class SequenceTraits { public: static Error emitSeparator(ChannelT &C) { return Error::success(); } static Error consumeSeparator(ChannelT &C) { return Error::success(); } }; /// Utility class for serializing sequences of values of varying types. /// Specializations of this class contain 'serialize' and 'deserialize' methods /// for the given channel. The ArgTs... list will determine the "over-the-wire" /// types to be serialized. The serialize and deserialize methods take a list /// CArgTs... ("caller arg types") which must be the same length as ArgTs..., /// but may be different types from ArgTs, provided that for each CArgT there /// is a SerializationTraits specialization /// SerializeTraits with methods that can serialize the /// caller argument to over-the-wire value. template class SequenceSerialization; template class SequenceSerialization { public: static Error serialize(ChannelT &C) { return Error::success(); } static Error deserialize(ChannelT &C) { return Error::success(); } }; template class SequenceSerialization { public: template static Error serialize(ChannelT &C, CArgT &&CArg) { return SerializationTraits::type>:: serialize(C, std::forward(CArg)); } template static Error deserialize(ChannelT &C, CArgT &CArg) { return SerializationTraits::deserialize(C, CArg); } }; template class SequenceSerialization { public: template static Error serialize(ChannelT &C, CArgT &&CArg, CArgTs &&... CArgs) { if (auto Err = SerializationTraits::type>:: serialize(C, std::forward(CArg))) return Err; if (auto Err = SequenceTraits::emitSeparator(C)) return Err; return SequenceSerialization:: serialize(C, std::forward(CArgs)...); } template static Error deserialize(ChannelT &C, CArgT &CArg, CArgTs &... CArgs) { if (auto Err = SerializationTraits::deserialize(C, CArg)) return Err; if (auto Err = SequenceTraits::consumeSeparator(C)) return Err; return SequenceSerialization::deserialize(C, CArgs...); } }; template Error serializeSeq(ChannelT &C, ArgTs &&... Args) { return SequenceSerialization::type...>:: serialize(C, std::forward(Args)...); } template Error deserializeSeq(ChannelT &C, ArgTs &... Args) { return SequenceSerialization::deserialize(C, Args...); } template class SerializationTraits { public: using WrappedErrorSerializer = std::function; using WrappedErrorDeserializer = std::function; template static void registerErrorType(std::string Name, SerializeFtor Serialize, DeserializeFtor Deserialize) { assert(!Name.empty() && "The empty string is reserved for the Success value"); const std::string *KeyName = nullptr; { // We're abusing the stability of std::map here: We take a reference to the // key of the deserializers map to save us from duplicating the string in // the serializer. This should be changed to use a stringpool if we switch // to a map type that may move keys in memory. std::lock_guard Lock(DeserializersMutex); auto I = Deserializers.insert(Deserializers.begin(), std::make_pair(std::move(Name), std::move(Deserialize))); KeyName = &I->first; } { assert(KeyName != nullptr && "No keyname pointer"); std::lock_guard Lock(SerializersMutex); Serializers[ErrorInfoT::classID()] = [KeyName, Serialize = std::move(Serialize)]( ChannelT &C, const ErrorInfoBase &EIB) -> Error { assert(EIB.dynamicClassID() == ErrorInfoT::classID() && "Serializer called for wrong error type"); if (auto Err = serializeSeq(C, *KeyName)) return Err; return Serialize(C, static_cast(EIB)); }; } } static Error serialize(ChannelT &C, Error &&Err) { std::lock_guard Lock(SerializersMutex); if (!Err) return serializeSeq(C, std::string()); return handleErrors(std::move(Err), [&C](const ErrorInfoBase &EIB) { auto SI = Serializers.find(EIB.dynamicClassID()); if (SI == Serializers.end()) return serializeAsStringError(C, EIB); return (SI->second)(C, EIB); }); } static Error deserialize(ChannelT &C, Error &Err) { std::lock_guard Lock(DeserializersMutex); std::string Key; if (auto Err = deserializeSeq(C, Key)) return Err; if (Key.empty()) { ErrorAsOutParameter EAO(&Err); Err = Error::success(); return Error::success(); } auto DI = Deserializers.find(Key); assert(DI != Deserializers.end() && "No deserializer for error type"); return (DI->second)(C, Err); } private: static Error serializeAsStringError(ChannelT &C, const ErrorInfoBase &EIB) { std::string ErrMsg; { raw_string_ostream ErrMsgStream(ErrMsg); EIB.log(ErrMsgStream); } return serialize(C, make_error(std::move(ErrMsg), inconvertibleErrorCode())); } static std::recursive_mutex SerializersMutex; static std::recursive_mutex DeserializersMutex; static std::map Serializers; static std::map Deserializers; }; template std::recursive_mutex SerializationTraits::SerializersMutex; template std::recursive_mutex SerializationTraits::DeserializersMutex; template std::map::WrappedErrorSerializer> SerializationTraits::Serializers; template std::map::WrappedErrorDeserializer> SerializationTraits::Deserializers; /// Registers a serializer and deserializer for the given error type on the /// given channel type. template void registerErrorSerialization(std::string Name, SerializeFtor &&Serialize, DeserializeFtor &&Deserialize) { SerializationTraits::template registerErrorType( std::move(Name), std::forward(Serialize), std::forward(Deserialize)); } /// Registers serialization/deserialization for StringError. template void registerStringError() { static bool AlreadyRegistered = false; if (!AlreadyRegistered) { registerErrorSerialization( "StringError", [](ChannelT &C, const StringError &SE) { return serializeSeq(C, SE.getMessage()); }, [](ChannelT &C, Error &Err) -> Error { ErrorAsOutParameter EAO(&Err); std::string Msg; if (auto E2 = deserializeSeq(C, Msg)) return E2; Err = make_error(std::move(Msg), orcError( OrcErrorCode::UnknownErrorCodeFromRemote)); return Error::success(); }); AlreadyRegistered = true; } } /// SerializationTraits for Expected from an Expected. template class SerializationTraits, Expected> { public: static Error serialize(ChannelT &C, Expected &&ValOrErr) { if (ValOrErr) { if (auto Err = serializeSeq(C, true)) return Err; return SerializationTraits::serialize(C, *ValOrErr); } if (auto Err = serializeSeq(C, false)) return Err; return serializeSeq(C, ValOrErr.takeError()); } static Error deserialize(ChannelT &C, Expected &ValOrErr) { ExpectedAsOutParameter EAO(&ValOrErr); bool HasValue; if (auto Err = deserializeSeq(C, HasValue)) return Err; if (HasValue) return SerializationTraits::deserialize(C, *ValOrErr); Error Err = Error::success(); if (auto E2 = deserializeSeq(C, Err)) return E2; ValOrErr = std::move(Err); return Error::success(); } }; /// SerializationTraits for Expected from a T2. template class SerializationTraits, T2> { public: static Error serialize(ChannelT &C, T2 &&Val) { return serializeSeq(C, Expected(std::forward(Val))); } }; /// SerializationTraits for Expected from an Error. template class SerializationTraits, Error> { public: static Error serialize(ChannelT &C, Error &&Err) { return serializeSeq(C, Expected(std::move(Err))); } }; /// SerializationTraits default specialization for std::pair. template class SerializationTraits, std::pair> { public: static Error serialize(ChannelT &C, const std::pair &V) { if (auto Err = SerializationTraits::serialize(C, V.first)) return Err; return SerializationTraits::serialize(C, V.second); } static Error deserialize(ChannelT &C, std::pair &V) { if (auto Err = SerializationTraits::deserialize(C, V.first)) return Err; return SerializationTraits::deserialize(C, V.second); } }; /// SerializationTraits default specialization for std::tuple. template class SerializationTraits> { public: /// RPC channel serialization for std::tuple. static Error serialize(ChannelT &C, const std::tuple &V) { return serializeTupleHelper(C, V, std::index_sequence_for()); } /// RPC channel deserialization for std::tuple. static Error deserialize(ChannelT &C, std::tuple &V) { return deserializeTupleHelper(C, V, std::index_sequence_for()); } private: // Serialization helper for std::tuple. template static Error serializeTupleHelper(ChannelT &C, const std::tuple &V, std::index_sequence _) { return serializeSeq(C, std::get(V)...); } // Serialization helper for std::tuple. template static Error deserializeTupleHelper(ChannelT &C, std::tuple &V, std::index_sequence _) { return deserializeSeq(C, std::get(V)...); } }; /// SerializationTraits default specialization for std::vector. template class SerializationTraits> { public: /// Serialize a std::vector from std::vector. static Error serialize(ChannelT &C, const std::vector &V) { if (auto Err = serializeSeq(C, static_cast(V.size()))) return Err; for (const auto &E : V) if (auto Err = serializeSeq(C, E)) return Err; return Error::success(); } /// Deserialize a std::vector to a std::vector. static Error deserialize(ChannelT &C, std::vector &V) { assert(V.empty() && "Expected default-constructed vector to deserialize into"); uint64_t Count = 0; if (auto Err = deserializeSeq(C, Count)) return Err; V.resize(Count); for (auto &E : V) if (auto Err = deserializeSeq(C, E)) return Err; return Error::success(); } }; template class SerializationTraits, std::set> { public: /// Serialize a std::set from std::set. static Error serialize(ChannelT &C, const std::set &S) { if (auto Err = serializeSeq(C, static_cast(S.size()))) return Err; for (const auto &E : S) if (auto Err = SerializationTraits::serialize(C, E)) return Err; return Error::success(); } /// Deserialize a std::set to a std::set. static Error deserialize(ChannelT &C, std::set &S) { assert(S.empty() && "Expected default-constructed set to deserialize into"); uint64_t Count = 0; if (auto Err = deserializeSeq(C, Count)) return Err; while (Count-- != 0) { T2 Val; if (auto Err = SerializationTraits::deserialize(C, Val)) return Err; auto Added = S.insert(Val).second; if (!Added) return make_error("Duplicate element in deserialized set", orcError(OrcErrorCode::UnknownORCError)); } return Error::success(); } }; template class SerializationTraits, std::map> { public: /// Serialize a std::map from std::map. static Error serialize(ChannelT &C, const std::map &M) { if (auto Err = serializeSeq(C, static_cast(M.size()))) return Err; for (const auto &E : M) { if (auto Err = SerializationTraits::serialize(C, E.first)) return Err; if (auto Err = SerializationTraits::serialize(C, E.second)) return Err; } return Error::success(); } /// Deserialize a std::map to a std::map. static Error deserialize(ChannelT &C, std::map &M) { assert(M.empty() && "Expected default-constructed map to deserialize into"); uint64_t Count = 0; if (auto Err = deserializeSeq(C, Count)) return Err; while (Count-- != 0) { std::pair Val; if (auto Err = SerializationTraits::deserialize(C, Val.first)) return Err; if (auto Err = SerializationTraits::deserialize(C, Val.second)) return Err; auto Added = M.insert(Val).second; if (!Added) return make_error("Duplicate element in deserialized map", orcError(OrcErrorCode::UnknownORCError)); } return Error::success(); } }; } // end namespace rpc } // end namespace orc } // end namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H