Add IPC support for mozilla::Variant (bug 1371846); r?botond draft
authorJeff Hajewski <jeff.hajewski@gmail.com>
Fri, 21 Jul 2017 07:18:02 -0500
changeset 614521 99bb755f42b61435911c067cba790fc1fd4f83af
parent 612966 0faada5c2f308f101ab7c54a87b3dce80b97d0e3
child 638887 c846dd68e0cd07f6c0cad667f12c37433e5f94ee
child 658010 5dc47847bc61dcda4a9156d051e2171a863d1f64
push id70036
push userjeff.hajewski@gmail.com
push dateMon, 24 Jul 2017 17:55:20 +0000
reviewersbotond
bugs1371846
milestone56.0a1
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
ipc/glue/IPCMessageUtils.h
mfbt/Variant.h
--- 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];