Bug 1440511 - Part 3: Add IPDLParamTraits which supports passing in an actor when {un,}pickling, r=froydnj
authorNika Layzell <nika@thelayzells.com>
Tue, 27 Feb 2018 16:37:53 -0500
changeset 406641 bbf6ac93e48c68af0259b9a638438e4644daf117
parent 406640 36db285e47c57d3b7afd14dabf059ee3bd53de31
child 406642 5467d67a257763bf7d30c364021e1b7b4bbbdb90
push id33572
push usercsabou@mozilla.com
push dateTue, 06 Mar 2018 04:27:41 +0000
treeherdermozilla-central@19838b896cd7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1440511
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1440511 - Part 3: Add IPDLParamTraits which supports passing in an actor when {un,}pickling, r=froydnj MozReview-Commit-ID: JTsQJ292A09
ipc/glue/IPDLParamTraits.h
ipc/glue/moz.build
new file mode 100644
--- /dev/null
+++ b/ipc/glue/IPDLParamTraits.h
@@ -0,0 +1,154 @@
+#ifndef mozilla_ipc_IPDLParamTraits_h
+#define mozilla_ipc_IPDLParamTraits_h
+
+#include "chrome/common/ipc_message_utils.h"
+
+namespace mozilla {
+namespace ipc {
+
+class IProtocol;
+
+//
+// IPDLParamTraits are an extended version of ParamTraits. Unlike ParamTraits,
+// IPDLParamTraits supports passing an additional IProtocol* argument to the
+// write and read methods.
+//
+// This is important for serializing and deserializing types which require
+// knowledge of which protocol they're being sent over, such as actors and
+// nsIInputStreams.
+//
+// All types which already implement ParamTraits also support IPDLParamTraits.
+//
+template<typename P>
+struct IPDLParamTraits
+{
+  // This is the default impl which discards the actor parameter and calls into
+  // ParamTraits. Types which want to use the actor parameter must specialize
+  // IPDLParamTraits.
+  static inline void Write(IPC::Message* aMsg, IProtocol*, const P& aParam) {
+    IPC::ParamTraits<P>::Write(aMsg, aParam);
+  }
+  // Some types which implement ParamTraits require non-const references, as
+  // they move their data into the IPC layer. This overload supports these
+  // types.
+  static inline void Write(IPC::Message* aMsg, IProtocol*, P& aParam) {
+    IPC::ParamTraits<P>::Write(aMsg, aParam);
+  }
+
+  static inline bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                          IProtocol*, P* aResult) {
+    return IPC::ParamTraits<P>::Read(aMsg, aIter, aResult);
+  }
+};
+
+//
+// WriteIPDLParam and ReadIPDLParam are like IPC::WriteParam and IPC::ReadParam,
+// however, they also accept an extra actor argument, and use IPDLParamTraits
+// rather than ParamTraits.
+//
+// NOTE: WriteIPDLParam takes a universal reference, so that it can support
+// whatever reference type is supported by the underlying IPDLParamTraits::Write
+// implementation. See the comment on IPDLParamTraits<nsTArray<T>>::Write for
+// more information.
+//
+template<typename P>
+static inline void
+WriteIPDLParam(IPC::Message* aMsg,
+               IProtocol* aActor,
+               P&& aParam)
+{
+  IPDLParamTraits<typename Decay<P>::Type>::Write(aMsg, aActor, Forward<P>(aParam));
+}
+
+template<typename P>
+static inline bool
+ReadIPDLParam(const IPC::Message* aMsg,
+              PickleIterator* aIter,
+              IProtocol* aActor,
+              P* aResult)
+{
+  return IPDLParamTraits<P>::Read(aMsg, aIter, aActor, aResult);
+}
+
+// nsTArray support for IPDLParamTraits
+template<typename T>
+struct IPDLParamTraits<nsTArray<T>>
+{
+  static inline void Write(IPC::Message* aMsg, IProtocol* aActor,
+                           const nsTArray<T>& aParam) {
+    WriteInternal(aMsg, aActor, aParam);
+  }
+
+  // Some serializers need to take a mutable reference to their backing object,
+  // such as Shmem segments and Byte Buffers. These serializers take the backing
+  // data and move it into the IPC layer for efficiency. They currently take
+  // these references as mutable lvalue references rather than rvalue
+  // references, (bug 1441651). This overload of Write on nsTArray is needed, as
+  // occasionally these types appear inside of IPDL arrays.
+  static inline void Write(IPC::Message* aMsg, IProtocol* aActor,
+                           nsTArray<T>& aParam) {
+    WriteInternal(aMsg, aActor, aParam);
+  }
+
+  // This method uses infallible allocation so that an OOM failure will
+  // show up as an OOM crash rather than an IPC FatalError.
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, nsTArray<T>* aResult)
+  {
+    uint32_t length;
+    if (!ReadIPDLParam(aMsg, aIter, aActor, &length)) {
+      return false;
+    }
+
+    if (sUseWriteBytes) {
+      auto pickledLength = CheckedInt<int>(length) * sizeof(T);
+      if (!pickledLength.isValid()) {
+        return false;
+      }
+
+      T* elements = aResult->AppendElements(length);
+      return aMsg->ReadBytesInto(aIter, elements, pickledLength.value());
+    }
+
+    aResult->SetCapacity(length);
+
+    for (uint32_t index = 0; index < length; index++) {
+      T* element = aResult->AppendElement();
+      if (!ReadIPDLParam(aMsg, aIter, aActor, element)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+private:
+  template<typename U>
+  static inline void
+  WriteInternal(IPC::Message* aMsg, IProtocol* aActor, U&& aParam) {
+    uint32_t length = aParam.Length();
+    WriteIPDLParam(aMsg, aActor, length);
+
+    if (sUseWriteBytes) {
+      auto pickledLength = CheckedInt<int>(length) * sizeof(T);
+      MOZ_RELEASE_ASSERT(pickledLength.isValid());
+      aMsg->WriteBytes(aParam.Elements(), pickledLength.value());
+    } else {
+      for (uint32_t index = 0; index < length; index++) {
+        WriteIPDLParam(aMsg, aActor, aParam.Elements()[index]);
+      }
+    }
+  }
+
+  // We write arrays of integer or floating-point data using a single pickling
+  // call, rather than writing each element individually.  We deliberately do
+  // not use mozilla::IsPod here because it is perfectly reasonable to have
+  // a data structure T for which IsPod<T>::value is true, yet also have a
+  // {IPDL,}ParamTraits<T> specialization.
+  static const bool sUseWriteBytes = (mozilla::IsIntegral<T>::value ||
+                                      mozilla::IsFloatingPoint<T>::value);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // defined(mozilla_ipc_IPDLParamTraits_h)
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -28,16 +28,17 @@ EXPORTS.mozilla.ipc += [
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
     'InputStreamUtils.h',
     'IOThreadChild.h',
     'IPCStreamAlloc.h',
     'IPCStreamDestination.h',
     'IPCStreamSource.h',
     'IPCStreamUtils.h',
+    'IPDLParamTraits.h',
     'MessageChannel.h',
     'MessageLink.h',
     'Neutering.h',
     'ProcessChild.h',
     'ProtocolUtils.h',
     'ScopedXREEmbed.h',
     'SharedMemory.h',
     'SharedMemoryBasic.h',