summaryrefslogtreecommitdiffstats
path: root/ipc/glue/IPDLParamTraits.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ipc/glue/IPDLParamTraits.h444
1 files changed, 444 insertions, 0 deletions
diff --git a/ipc/glue/IPDLParamTraits.h b/ipc/glue/IPDLParamTraits.h
new file mode 100644
index 0000000000..f258bdcaf6
--- /dev/null
+++ b/ipc/glue/IPDLParamTraits.h
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_IPDLParamTraits_h
+#define mozilla_ipc_IPDLParamTraits_h
+
+#include "chrome/common/ipc_message_utils.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Variant.h"
+#include "mozilla/Tuple.h"
+#include "nsTArray.h"
+
+#include <type_traits>
+
+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.
+ template <typename R>
+ static inline void Write(IPC::Message* aMsg, IProtocol*, R&& aParam) {
+ IPC::ParamTraits<P>::Write(aMsg, std::forward<R>(aParam));
+ }
+
+ template <typename R>
+ static inline bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol*, R* 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 MOZ_NEVER_INLINE void WriteIPDLParam(IPC::Message* aMsg,
+ IProtocol* aActor, P&& aParam) {
+ IPDLParamTraits<std::decay_t<P>>::Write(aMsg, aActor,
+ std::forward<P>(aParam));
+}
+
+template <typename P>
+static MOZ_NEVER_INLINE bool ReadIPDLParam(const IPC::Message* aMsg,
+ PickleIterator* aIter,
+ IProtocol* aActor, P* aResult) {
+ return IPDLParamTraits<P>::Read(aMsg, aIter, aActor, aResult);
+}
+
+template <typename P>
+static MOZ_NEVER_INLINE bool ReadIPDLParamInfallible(
+ const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
+ P* aResult, const char* aCrashMessage) {
+ bool ok = ReadIPDLParam(aMsg, aIter, aActor, aResult);
+ if (!ok) {
+ MOZ_CRASH_UNSAFE(aCrashMessage);
+ }
+ return ok;
+}
+
+constexpr void WriteIPDLParamList(IPC::Message*, IProtocol*) {}
+
+template <typename P, typename... Ps>
+static void WriteIPDLParamList(IPC::Message* aMsg, IProtocol* aActor,
+ P&& aParam, Ps&&... aParams) {
+ WriteIPDLParam(aMsg, aActor, std::forward<P>(aParam));
+ WriteIPDLParamList(aMsg, aActor, std::forward<Ps>(aParams)...);
+}
+
+constexpr bool ReadIPDLParamList(const IPC::Message*, PickleIterator*,
+ IProtocol*) {
+ return true;
+}
+
+template <typename P, typename... Ps>
+static bool ReadIPDLParamList(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, P* aResult, Ps*... aResults) {
+ return ReadIPDLParam(aMsg, aIter, aActor, aResult) &&
+ ReadIPDLParamList(aMsg, aIter, aActor, aResults...);
+}
+
+// When being passed `RefPtr<T>` or `nsCOMPtr<T>`, forward to a specialization
+// for the underlying target type. The parameter type will be passed as `T*`,
+// and result as `RefPtr<T>*`.
+//
+// This is done explicitly to ensure that the deleted `&&` overload for
+// `operator T*` is not selected in generic contexts, and to support
+// deserializing into `nsCOMPtr<T>`.
+template <typename T>
+struct IPDLParamTraits<RefPtr<T>> {
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const RefPtr<T>& aParam) {
+ IPDLParamTraits<T*>::Write(aMsg, aActor, aParam.get());
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, RefPtr<T>* aResult) {
+ return IPDLParamTraits<T*>::Read(aMsg, aIter, aActor, aResult);
+ }
+};
+
+template <typename T>
+struct IPDLParamTraits<nsCOMPtr<T>> {
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const nsCOMPtr<T>& aParam) {
+ IPDLParamTraits<T*>::Write(aMsg, aActor, aParam.get());
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, nsCOMPtr<T>* aResult) {
+ RefPtr<T> refptr;
+ if (!IPDLParamTraits<T*>::Read(aMsg, aIter, aActor, &refptr)) {
+ return false;
+ }
+ *aResult = refptr.forget();
+ return true;
+ }
+};
+
+// nsTArray support for IPDLParamTraits
+template <typename T>
+struct IPDLParamTraits<nsTArray<T>> {
+ // 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. `Write` uses a
+ // forwarding reference as occasionally these types appear inside of IPDL
+ // arrays.
+ template <typename U>
+ static void Write(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 {
+ WriteValues(aMsg, aActor, std::forward<U>(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() ||
+ !aMsg->HasBytesAvailable(aIter, pickledLength.value())) {
+ return false;
+ }
+
+ // XXX(nika): This currently default-constructs the backing data before
+ // passing it into ReadBytesInto, which is technically unnecessary here.
+ // Perhaps we should consider using an API which doesn't initialize the
+ // elements?
+ T* elements = aResult->AppendElements(length);
+ return aMsg->ReadBytesInto(aIter, elements, pickledLength.value());
+ }
+
+ // Each ReadIPDLParam<E> may read more than 1 byte each; this is an attempt
+ // to minimally validate that the length isn't much larger than what's
+ // actually available in aMsg. We cannot use |pickledLength|, like in the
+ // codepath above, because ReadIPDLParam can read variable amounts of data
+ // from aMsg.
+ if (!aMsg->HasBytesAvailable(aIter, length)) {
+ return false;
+ }
+
+ 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:
+ // Length has already been written. Const overload.
+ static void WriteValues(IPC::Message* aMsg, IProtocol* aActor,
+ const nsTArray<T>& aParam) {
+ for (auto& elt : aParam) {
+ WriteIPDLParam(aMsg, aActor, elt);
+ }
+ }
+
+ // Length has already been written. Rvalue overload.
+ static void WriteValues(IPC::Message* aMsg, IProtocol* aActor,
+ nsTArray<T>&& aParam) {
+ for (auto& elt : aParam) {
+ WriteIPDLParam(aMsg, aActor, std::move(elt));
+ }
+
+ // As we just moved all of our values out, let's clean up after ourselves &
+ // clear the input array. This means our move write method will act more
+ // like a traditional move constructor.
+ aParam.Clear();
+ }
+
+ // 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 =
+ (std::is_integral_v<T> || std::is_floating_point_v<T>);
+};
+
+template <typename T>
+struct IPDLParamTraits<CopyableTArray<T>> : IPDLParamTraits<nsTArray<T>> {};
+
+// Maybe support for IPDLParamTraits
+template <typename T>
+struct IPDLParamTraits<Maybe<T>> {
+ typedef Maybe<T> paramType;
+
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const Maybe<T>& aParam) {
+ bool isSome = aParam.isSome();
+ WriteIPDLParam(aMsg, aActor, isSome);
+
+ if (isSome) {
+ WriteIPDLParam(aMsg, aActor, aParam.ref());
+ }
+ }
+
+ static void Write(IPC::Message* aMsg, IProtocol* aActor, Maybe<T>&& aParam) {
+ bool isSome = aParam.isSome();
+ WriteIPDLParam(aMsg, aActor, isSome);
+
+ if (isSome) {
+ WriteIPDLParam(aMsg, aActor, std::move(aParam.ref()));
+ }
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, Maybe<T>* aResult) {
+ bool isSome;
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &isSome)) {
+ return false;
+ }
+
+ if (isSome) {
+ aResult->emplace();
+ if (!ReadIPDLParam(aMsg, aIter, aActor, aResult->ptr())) {
+ return false;
+ }
+ } else {
+ aResult->reset();
+ }
+ return true;
+ }
+};
+
+template <typename T>
+struct IPDLParamTraits<UniquePtr<T>> {
+ typedef UniquePtr<T> paramType;
+
+ template <typename U>
+ static void Write(IPC::Message* aMsg, IProtocol* aActor, U&& aParam) {
+ bool isNull = aParam == nullptr;
+ WriteIPDLParam(aMsg, aActor, isNull);
+
+ if (!isNull) {
+ WriteValue(aMsg, aActor, std::forward<U>(aParam));
+ }
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, UniquePtr<T>* aResult) {
+ bool isNull = true;
+ if (!ReadParam(aMsg, aIter, &isNull)) {
+ return false;
+ }
+
+ if (isNull) {
+ aResult->reset();
+ } else {
+ *aResult = MakeUnique<T>();
+ if (!ReadIPDLParam(aMsg, aIter, aActor, aResult->get())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ // If we have an rvalue, clear out our passed-in parameter.
+ static void WriteValue(IPC::Message* aMsg, IProtocol* aActor,
+ UniquePtr<T>&& aParam) {
+ WriteIPDLParam(aMsg, aActor, std::move(*aParam.get()));
+ aParam = nullptr;
+ }
+
+ static void WriteValue(IPC::Message* aMsg, IProtocol* aActor,
+ const UniquePtr<T>& aParam) {
+ WriteIPDLParam(aMsg, aActor, *aParam.get());
+ }
+};
+
+template <typename... Ts>
+struct IPDLParamTraits<Tuple<Ts...>> {
+ typedef Tuple<Ts...> paramType;
+
+ template <typename U>
+ static void Write(IPC::Message* aMsg, IProtocol* aActor, U&& aParam) {
+ WriteInternal(aMsg, aActor, std::forward<U>(aParam),
+ std::index_sequence_for<Ts...>{});
+ }
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, Tuple<Ts...>* aResult) {
+ return ReadInternal(aMsg, aIter, aActor, *aResult,
+ std::index_sequence_for<Ts...>{});
+ }
+
+ private:
+ template <size_t... Is>
+ static void WriteInternal(IPC::Message* aMsg, IProtocol* aActor,
+ const Tuple<Ts...>& aParam,
+ std::index_sequence<Is...>) {
+ WriteIPDLParamList(aMsg, aActor, Get<Is>(aParam)...);
+ }
+
+ template <size_t... Is>
+ static void WriteInternal(IPC::Message* aMsg, IProtocol* aActor,
+ Tuple<Ts...>&& aParam, std::index_sequence<Is...>) {
+ WriteIPDLParamList(aMsg, aActor, std::move(Get<Is>(aParam))...);
+ }
+
+ template <size_t... Is>
+ static bool ReadInternal(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, Tuple<Ts...>& aResult,
+ std::index_sequence<Is...>) {
+ return ReadIPDLParamList(aMsg, aIter, aActor, &Get<Is>(aResult)...);
+ }
+};
+
+template <class... Ts>
+struct IPDLParamTraits<mozilla::Variant<Ts...>> {
+ typedef mozilla::Variant<Ts...> paramType;
+ using Tag = typename mozilla::detail::VariantTag<Ts...>::Type;
+
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.tag);
+ aParam.match(
+ [aMsg, aActor](const auto& t) { WriteIPDLParam(aMsg, aActor, t); });
+ }
+
+ static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.tag);
+ aParam.match([aMsg, aActor](auto& t) {
+ WriteIPDLParam(aMsg, aActor, std::move(t));
+ });
+ }
+
+ // 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 IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, Tag aTag, paramType* aResult) {
+ // 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 (aTag == N - 1) {
+ // Recall, even though the template parameter is N, we are
+ // actually interested in the N - 1 tag.
+ // Default construct our field within the result outparameter and
+ // directly deserialize into the variant. Note that this means that
+ // every type in Ts needs to be default constructible.
+ return ReadIPDLParam(aMsg, aIter, aActor,
+ &aResult->template emplace<N - 1>());
+ }
+ return Next::Read(aMsg, aIter, aActor, aTag, aResult);
+ }
+
+ }; // 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 IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, Tag aTag, paramType* aResult) {
+ return false;
+ }
+ };
+
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ Tag tag;
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &tag)) {
+ return false;
+ }
+
+ return VariantReader<sizeof...(Ts)>::Read(aMsg, aIter, aActor, tag,
+ aResult);
+ }
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // defined(mozilla_ipc_IPDLParamTraits_h)