Bug 1584256 - Add IPDLParamTraits for Variant. r=nika,jwalden

Differential Revision: https://phabricator.services.mozilla.com/D47607

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Peter Van der Beken 2019-10-08 16:03:11 +00:00
parent 183f92d2ca
commit 22e4870bda
4 changed files with 159 additions and 9 deletions

View File

@ -992,12 +992,10 @@ struct ParamTraits<mozilla::Variant<Ts...>> {
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 = mozilla::AsVariant(val);
return true;
}
return false;
// 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 ReadParam(msg, iter, &result->template emplace<N - 1>());
} else {
return Next::Read(msg, iter, tag, result);
}

View File

@ -9,6 +9,7 @@
#include "chrome/common/ipc_message_utils.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
namespace mozilla {
namespace ipc {
@ -350,6 +351,75 @@ struct IPDLParamTraits<Tuple<Ts...>> {
}
};
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

View File

@ -27,6 +27,11 @@ struct ParamTraits;
namespace mozilla {
namespace ipc {
template <typename T>
struct IPDLParamTraits;
} // namespace ipc
template <typename... Ts>
class Variant;
@ -505,6 +510,7 @@ struct VariantIndex {
template <typename... Ts>
class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
friend struct IPC::ParamTraits<mozilla::Variant<Ts...>>;
friend struct mozilla::ipc::IPDLParamTraits<mozilla::Variant<Ts...>>;
using Tag = typename detail::VariantTag<Ts...>::Type;
using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
@ -622,6 +628,23 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
~Variant() { Impl::destroy(*this); }
template <typename T, typename... Args>
T& emplace(Args&&... aTs) {
Impl::destroy(*this);
tag = Impl::template tag<T>();
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
return as<T>();
}
template <size_t N, typename... Args>
typename detail::Nth<N, Ts...>::Type& emplace(Args&&... aTs) {
using T = typename detail::Nth<N, Ts...>::Type;
Impl::destroy(*this);
tag = N;
::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
return as<N>();
}
/** Check which variant type is currently contained. */
template <typename T>
bool is() const {

View File

@ -281,6 +281,52 @@ static void testConstructionWithVariantIndex() {
MOZ_RELEASE_ASSERT(v.extract<2>() == 2);
}
static void testEmplaceWithType() {
printf("testEmplaceWithType\n");
Variant<uint32_t, uint64_t, uint32_t> v1(mozilla::VariantIndex<0>{}, 0);
v1.emplace<uint64_t>(3);
MOZ_RELEASE_ASSERT(v1.is<uint64_t>());
MOZ_RELEASE_ASSERT(v1.as<uint64_t>() == 3);
Variant<UniquePtr<int>, char> v2('a');
v2.emplace<UniquePtr<int>>();
MOZ_RELEASE_ASSERT(v2.is<UniquePtr<int>>());
MOZ_RELEASE_ASSERT(!v2.as<UniquePtr<int>>().get());
Variant<UniquePtr<int>, char> v3('a');
v3.emplace<UniquePtr<int>>(MakeUnique<int>(4));
MOZ_RELEASE_ASSERT(v3.is<UniquePtr<int>>());
MOZ_RELEASE_ASSERT(*v3.as<UniquePtr<int>>().get() == 4);
}
static void testEmplaceWithIndex() {
printf("testEmplaceWithIndex\n");
Variant<uint32_t, uint64_t, uint32_t> v1(mozilla::VariantIndex<1>{}, 0);
v1.emplace<2>(2);
MOZ_RELEASE_ASSERT(!v1.is<uint64_t>());
MOZ_RELEASE_ASSERT(!v1.is<1>());
MOZ_RELEASE_ASSERT(!v1.is<0>());
MOZ_RELEASE_ASSERT(v1.is<2>());
MOZ_RELEASE_ASSERT(v1.as<2>() == 2);
MOZ_RELEASE_ASSERT(v1.extract<2>() == 2);
Variant<UniquePtr<int>, char> v2('a');
v2.emplace<0>();
MOZ_RELEASE_ASSERT(v2.is<UniquePtr<int>>());
MOZ_RELEASE_ASSERT(!v2.is<1>());
MOZ_RELEASE_ASSERT(v2.is<0>());
MOZ_RELEASE_ASSERT(!v2.as<0>().get());
MOZ_RELEASE_ASSERT(!v2.extract<0>().get());
Variant<UniquePtr<int>, char> v3('a');
v3.emplace<0>(MakeUnique<int>(4));
MOZ_RELEASE_ASSERT(v3.is<UniquePtr<int>>());
MOZ_RELEASE_ASSERT(!v3.is<1>());
MOZ_RELEASE_ASSERT(v3.is<0>());
MOZ_RELEASE_ASSERT(*v3.as<0>().get() == 4);
MOZ_RELEASE_ASSERT(*v3.extract<0>().get() == 4);
}
static void testCopy() {
printf("testCopy\n");
Variant<uint32_t, uint64_t> v1(uint64_t(1));
@ -328,15 +374,26 @@ static void testDestructor() {
Destroyer d;
{
Variant<char, UniquePtr<char[]>, Destroyer> v(d);
Variant<char, UniquePtr<char[]>, Destroyer> v1(d);
MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0); // None detroyed yet.
}
MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
1); // v's copy of d is destroyed.
1); // v1's copy of d is destroyed.
{
Variant<char, UniquePtr<char[]>, Destroyer> v2(
mozilla::VariantIndex<2>{});
v2.emplace<Destroyer>(d);
MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
2); // v2's initial value is destroyed.
}
MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
3); // v2's second value is destroyed.
}
MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 2); // d is destroyed.
MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 4); // d is destroyed.
}
static void testEquality() {
@ -510,6 +567,8 @@ int main() {
testDuplicate();
testConstructionWithVariantType();
testConstructionWithVariantIndex();
testEmplaceWithType();
testEmplaceWithIndex();
testCopy();
testMove();
testDestructor();