Add IPC support for mozilla::Variant (
bug 1371846); r?botond
Changes made:
* Add IPC::ParamTraits as a friend to mozilla::Variant in Variant.h.
This is required so that `tag` can be accessed in the
IPC::ParamTraits specialization.
* Add a IPC::ParamTraits specialization to IPCMessageUtils.h.
MozReview-Commit-ID: B3pGrZE1z0O
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -898,11 +898,87 @@ struct ParamTraits<mozilla::Maybe<T>>
*result = mozilla::Some(mozilla::Move(tmp));
} else {
*result = mozilla::Nothing();
}
return true;
}
};
+template<class... Ts>
+struct ParamTraits<mozilla::Variant<Ts...>>
+{
+ typedef mozilla::Variant<Ts...> paramType;
+ using Tag = typename mozilla::detail::VariantTag<Ts...>::Type;
+
+ struct VariantWriter
+ {
+ Message* msg;
+
+ template<class T>
+ void match(const T& t) {
+ WriteParam(msg, t);
+ }
+ };
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ WriteParam(msg, param.tag);
+ param.match(VariantWriter(msg));
+ }
+
+ // Because VariantReader is a nested struct, we need the dummy template
+ // parameter to avoid making VariantReader<0> an explicit specialization,
+ // which is not allowed for a nested class template
+ template<size_t N, typename dummy = void>
+ struct VariantReader
+ {
+ using Next = VariantReader<N-1>;
+
+ static bool Read(const Message* msg, PickleIterator* iter,
+ Tag tag, paramType* result)
+ {
+ // Since the VariantReader specializations start at N , we need to
+ // subtract one to look at N - 1, the first valid tag. This means our
+ // comparisons are off by 1. If we get to N = 0 then we have failed to
+ // find a match to the tag.
+ if (tag == N - 1) {
+ // Recall, even though the template parameter is N, we are
+ // actually interested in the N - 1 tag.
+ typename mozilla::detail::Nth<N - 1, Ts...>::Type val;
+ if (ReadParam(msg, iter, &val)) {
+ *result = paramType::AsVariant(val);
+ return true;
+ }
+ return false;
+ } else {
+ return Next::Read(msg, iter, tag);
+ }
+ }
+
+ }; // VariantReader<N>
+
+ // Since we are conditioning on tag = N - 1 in the preceding specialization,
+ // if we get to `VariantReader<0, dummy>` we have failed to find
+ // a matching tag.
+ template<typename dummy>
+ struct VariantReader<0, dummy>
+ {
+ static bool Read(const Message* msg, PickleIterator* iter,
+ Tag tag, paramType* result)
+ {
+ return false;
+ }
+ };
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ Tag tag;
+ if (ReadParam(msg, iter, &tag)) {
+ return VariantReader<sizeof...(Ts)>::Read(msg, iter, tag, result);
+ }
+ return false;
+ }
+};
+
} /* namespace IPC */
#endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
--- a/mfbt/Variant.h
+++ b/mfbt/Variant.h
@@ -13,16 +13,20 @@
#include "mozilla/Move.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/TemplateLib.h"
#include "mozilla/TypeTraits.h"
#ifndef mozilla_Variant_h
#define mozilla_Variant_h
+namespace IPC {
+template <typename T> struct ParamTraits;
+} // namespace IPC
+
namespace mozilla {
template<typename... Ts>
class Variant;
namespace detail {
// Nth<N, types...>::Type is the Nth type (0-based) in the list of types Ts.
@@ -479,16 +483,18 @@ template<size_t N> struct VariantIndex {
* and because |alignas| requirements don't affect platform ABI with respect to
* how parameters are laid out in memory, Variant can't be used as the type of a
* function parameter. Pass Variant to functions by pointer or reference
* instead.
*/
template<typename... Ts>
class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant
{
+ friend struct IPC::ParamTraits<mozilla::Variant<Ts...>>;
+
using Tag = typename detail::VariantTag<Ts...>::Type;
using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
static constexpr size_t RawDataAlignment = tl::Max<alignof(Ts)...>::value;
static constexpr size_t RawDataSize = tl::Max<sizeof(Ts)...>::value;
// Raw storage for the contained variant value.
alignas(RawDataAlignment) unsigned char rawData[RawDataSize];