Commit 5430084b authored by yzshen's avatar yzshen Committed by Commit bot

Mojo C++ bindings: introduce mojo::WTFMap for blink bindings.

BUG=583738

Review-Url: https://codereview.chromium.org/2034273002
Cr-Commit-Position: refs/heads/master@{#400223}
parent 5a24652e
......@@ -60,6 +60,7 @@
'public/cpp/bindings/tests/equals_unittest.cc',
'public/cpp/bindings/tests/handle_passing_unittest.cc',
'public/cpp/bindings/tests/interface_ptr_unittest.cc',
'public/cpp/bindings/tests/map_common_test.h',
'public/cpp/bindings/tests/map_unittest.cc',
'public/cpp/bindings/tests/message_queue.cc',
'public/cpp/bindings/tests/message_queue.h',
......@@ -133,8 +134,10 @@
'public/cpp/bindings/tests/array_common_test.h',
'public/cpp/bindings/tests/container_test_util.cc',
'public/cpp/bindings/tests/container_test_util.h',
'public/cpp/bindings/tests/map_common_test.h',
'public/cpp/bindings/tests/variant_test_util.h',
'public/cpp/bindings/tests/wtf_array_unittest.cc',
'public/cpp/bindings/tests/wtf_map_unittest.cc',
'public/cpp/bindings/tests/wtf_types_unittest.cc',
],
},
......
......@@ -230,8 +230,10 @@
'public/cpp/bindings/array_traits_wtf_vector.h',
'public/cpp/bindings/lib/string_traits_wtf.cc',
'public/cpp/bindings/lib/wtf_serialization.h',
'public/cpp/bindings/map_traits_wtf.h',
'public/cpp/bindings/string_traits_wtf.h',
'public/cpp/bindings/wtf_array.h',
'public/cpp/bindings/wtf_map.h',
],
'dependencies': [
'mojo_cpp_bindings',
......
......@@ -158,8 +158,10 @@ if (!is_ios) {
"array_traits_wtf_vector.h",
"lib/string_traits_wtf.cc",
"lib/wtf_serialization.h",
"map_traits_wtf.h",
"string_traits_wtf.h",
"wtf_array.h",
"wtf_map.h",
]
public_deps = [
......
......@@ -259,7 +259,7 @@ struct ArraySerializer<MojomType,
using Traits = ArrayTraits<UserType>;
using Data = typename MojomType::Data_;
static_assert(std::is_same<bool, typename UserType::Element>::value,
static_assert(std::is_same<bool, typename Traits::Element>::value,
"Incorrect array serializer");
static size_t GetSerializedSize(UserTypeIterator* input,
......
......@@ -6,6 +6,7 @@
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_
#include <type_traits>
#include <vector>
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/array_serialization.h"
......@@ -84,11 +85,11 @@ struct Serializer<Map<Key, Value>, MaybeConstUserType> {
using UserValue = typename Traits::Value;
using Data = typename Map<Key, Value>::Data_;
using KeyArraySerializer = ArraySerializer<Array<Key>,
Array<UserKey>,
std::vector<UserKey>,
MapKeyReader<MaybeConstUserType>>;
using ValueArraySerializer =
ArraySerializer<Array<Value>,
Array<UserValue>,
std::vector<UserValue>,
MapValueReader<MaybeConstUserType>>;
static size_t PrepareToSerialize(MaybeConstUserType& input,
......@@ -147,8 +148,8 @@ struct Serializer<Map<Key, Value>, MaybeConstUserType> {
if (!input)
return CallSetToNullIfExists<Traits>(output);
Array<UserKey> keys;
Array<UserValue> values;
std::vector<UserKey> keys;
std::vector<UserValue> values;
if (!KeyArraySerializer::DeserializeElements(input->keys.ptr, &keys,
context) ||
......@@ -161,8 +162,10 @@ struct Serializer<Map<Key, Value>, MaybeConstUserType> {
size_t size = keys.size();
Traits::SetToEmpty(output);
for (size_t i = 0; i < size; ++i)
Traits::Insert(*output, std::move(keys[i]), std::move(values[i]));
for (size_t i = 0; i < size; ++i) {
if (!Traits::Insert(*output, std::move(keys[i]), std::move(values[i])))
return false;
}
return true;
}
};
......
......@@ -7,6 +7,7 @@
#include "mojo/public/cpp/bindings/array_traits_wtf.h"
#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
#include "mojo/public/cpp/bindings/map_traits_wtf.h"
#include "mojo/public/cpp/bindings/string_traits_wtf.h"
#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_
......@@ -36,10 +36,12 @@ namespace mojo {
// static V& GetValue(CustomIterator& iterator);
// static const V& GetValue(CustomConstIterator& iterator);
//
// static void Insert(CustomMap<K, V>& input,
// // Returning false results in deserialization failure and causes the
// // message pipe receiving it to be disconnected.
// static bool Insert(CustomMap<K, V>& input,
// const K& key,
// V&& value);
// static void Insert(CustomMap<K, V>& input,
// static bool Insert(CustomMap<K, V>& input,
// const K& key,
// const V& value);
//
......
......@@ -36,11 +36,13 @@ struct MapTraits<Map<K, V>> {
static V& GetValue(Iterator& iterator) { return iterator->second; }
static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
static void Insert(Map<K, V>& input, const K& key, V&& value) {
static bool Insert(Map<K, V>& input, const K& key, V&& value) {
input.insert(key, std::forward<V>(value));
return true;
}
static void Insert(Map<K, V>& input, const K& key, const V& value) {
static bool Insert(Map<K, V>& input, const K& key, const V& value) {
input.insert(key, value);
return true;
}
static void SetToEmpty(Map<K, V>* output) { output->SetToEmpty(); }
......
......@@ -44,11 +44,13 @@ struct MapTraits<std::map<K, V>> {
static V& GetValue(Iterator& iterator) { return iterator->second; }
static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
static void Insert(std::map<K, V>& input, const K& key, V&& value) {
static bool Insert(std::map<K, V>& input, const K& key, V&& value) {
input.insert(std::make_pair(key, std::forward<V>(value)));
return true;
}
static void Insert(std::map<K, V>& input, const K& key, const V& value) {
static bool Insert(std::map<K, V>& input, const K& key, const V& value) {
input.insert(std::make_pair(key, value));
return true;
}
static void SetToEmpty(std::map<K, V>* output) { output->clear(); }
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
#include "base/logging.h"
#include "mojo/public/cpp/bindings/map_traits.h"
#include "mojo/public/cpp/bindings/wtf_map.h"
namespace mojo {
template <typename K, typename V>
struct MapTraits<WTFMap<K, V>> {
using Key = K;
using Value = V;
using Iterator = typename WTFMap<K, V>::Iterator;
using ConstIterator = typename WTFMap<K, V>::ConstIterator;
static bool IsNull(const WTFMap<K, V>& input) { return input.is_null(); }
static void SetToNull(WTFMap<K, V>* output) { *output = nullptr; }
static size_t GetSize(const WTFMap<K, V>& input) { return input.size(); }
static ConstIterator GetBegin(const WTFMap<K, V>& input) {
return input.begin();
}
static Iterator GetBegin(WTFMap<K, V>& input) { return input.begin(); }
static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
static void AdvanceIterator(Iterator& iterator) { ++iterator; }
static const K& GetKey(Iterator& iterator) { return iterator->key; }
static const K& GetKey(ConstIterator& iterator) { return iterator->key; }
static V& GetValue(Iterator& iterator) { return iterator->value; }
static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
static bool Insert(WTFMap<K, V>& input, const K& key, V&& value) {
if (!WTFMap<K, V>::IsValidKey(key)) {
LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
return false;
}
input.insert(key, std::forward<V>(value));
return true;
}
static bool Insert(WTFMap<K, V>& input, const K& key, const V& value) {
if (!WTFMap<K, V>::IsValidKey(key)) {
LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
return false;
}
input.insert(key, value);
return true;
}
static void SetToEmpty(WTFMap<K, V>* output) { output->SetToEmpty(); }
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
......@@ -23,6 +23,7 @@ source_set("tests") {
"equals_unittest.cc",
"handle_passing_unittest.cc",
"interface_ptr_unittest.cc",
"map_common_test.h",
"map_unittest.cc",
"message_queue.cc",
"message_queue.h",
......@@ -81,8 +82,10 @@ if (!is_ios) {
"array_common_test.h",
"container_test_util.cc",
"container_test_util.h",
"map_common_test.h",
"variant_test_util.h",
"wtf_array_unittest.cc",
"wtf_map_unittest.cc",
"wtf_types_unittest.cc",
]
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
#include "mojo/public/cpp/bindings/lib/validate_params.h"
#include "mojo/public/cpp/bindings/map.h"
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/tests/container_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace WTF {
class String;
}
namespace mojo {
template <typename T>
class WTFArray;
template <typename K, typename V>
class WTFMap;
namespace test {
namespace {
struct StringIntData {
const char* string_data;
int int_data;
} kStringIntData[] = {
{"one", 1},
{"two", 2},
{"three", 3},
{"four", 4},
};
const size_t kStringIntDataSize = 4;
} // namespace
template <template <typename...> class MapType>
struct TypeTraits;
template <>
struct TypeTraits<Map> {
using StringType = mojo::String;
template <typename T>
using ArrayType = Array<T>;
};
template <>
struct TypeTraits<WTFMap> {
using StringType = WTF::String;
template <typename T>
using ArrayType = WTFArray<T>;
};
// Common tests for both mojo::Map and mojo::WTFMap.
template <template <typename...> class MapType>
class MapCommonTest {
public:
using StringType = typename TypeTraits<MapType>::StringType;
template <typename T>
using ArrayType = typename TypeTraits<MapType>::template ArrayType<T>;
// Tests null and empty maps.
static void NullAndEmpty() {
MapType<char, char> map0;
EXPECT_TRUE(map0.empty());
EXPECT_FALSE(map0.is_null());
map0 = nullptr;
EXPECT_TRUE(map0.is_null());
EXPECT_FALSE(map0.empty());
MapType<char, char> map1(nullptr);
EXPECT_TRUE(map1.is_null());
EXPECT_FALSE(map1.empty());
map1.SetToEmpty();
EXPECT_TRUE(map1.empty());
EXPECT_FALSE(map1.is_null());
}
// Tests that basic Map operations work.
static void InsertWorks() {
MapType<StringType, int> map;
for (size_t i = 0; i < kStringIntDataSize; ++i)
map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
for (size_t i = 0; i < kStringIntDataSize; ++i) {
EXPECT_EQ(kStringIntData[i].int_data,
map.at(kStringIntData[i].string_data));
}
}
static void TestIndexOperator() {
MapType<StringType, int> map;
for (size_t i = 0; i < kStringIntDataSize; ++i)
map[kStringIntData[i].string_data] = kStringIntData[i].int_data;
for (size_t i = 0; i < kStringIntDataSize; ++i) {
EXPECT_EQ(kStringIntData[i].int_data,
map.at(kStringIntData[i].string_data));
}
}
static void TestIndexOperatorAsRValue() {
MapType<StringType, int> map;
for (size_t i = 0; i < kStringIntDataSize; ++i)
map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
for (size_t i = 0; i < kStringIntDataSize; ++i) {
EXPECT_EQ(kStringIntData[i].int_data, map[kStringIntData[i].string_data]);
}
}
static void TestIndexOperatorMoveOnly() {
ASSERT_EQ(0u, MoveOnlyType::num_instances());
MapType<StringType, ArrayType<int32_t>> map;
for (size_t i = 0; i < kStringIntDataSize; ++i) {
const char* key = kStringIntData[i].string_data;
ArrayType<int32_t> array(1);
array[0] = kStringIntData[i].int_data;
map[key] = std::move(array);
EXPECT_TRUE(map);
}
// We now read back that data, to test the behavior of operator[].
for (size_t i = 0; i < kStringIntDataSize; ++i) {
auto& value = map[kStringIntData[i].string_data];
ASSERT_EQ(1u, value.size());
EXPECT_EQ(kStringIntData[i].int_data, value[0]);
}
}
static void MapArrayClone() {
MapType<StringType, ArrayType<StringType>> m;
for (size_t i = 0; i < kStringIntDataSize; ++i) {
ArrayType<StringType> s(1);
s[0] = StringType(kStringIntData[i].string_data);
m.insert(kStringIntData[i].string_data, std::move(s));
}
MapType<StringType, ArrayType<StringType>> m2 = m.Clone();
for (size_t i = 0; i < kStringIntDataSize; ++i) {
ASSERT_NE(m2.end(), m2.find(kStringIntData[i].string_data));
ASSERT_EQ(1u, m2[kStringIntData[i].string_data].size());
EXPECT_EQ(StringType(kStringIntData[i].string_data),
m2[kStringIntData[i].string_data][0]);
}
}
static void ArrayOfMap() {
{
using MojomType = Array<Map<int32_t, int8_t>>;
using UserType = ArrayType<MapType<int32_t, int8_t>>;
UserType array(1);
array[0].insert(1, 42);
mojo::internal::SerializationContext context;
size_t size =
mojo::internal::PrepareToSerialize<MojomType>(array, &context);
mojo::internal::FixedBufferForTesting buf(size);
typename MojomType::Data_* data;
mojo::internal::ContainerValidateParams validate_params(
0, false,
new mojo::internal::ContainerValidateParams(
new mojo::internal::ContainerValidateParams(0, false, nullptr),
new mojo::internal::ContainerValidateParams(0, false, nullptr)));
mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
&context);
UserType deserialized_array;
mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
&context);
ASSERT_EQ(1u, deserialized_array.size());
ASSERT_EQ(1u, deserialized_array[0].size());
ASSERT_EQ(42, deserialized_array[0].at(1));
}
{
using MojomType = Array<Map<String, Array<bool>>>;
using UserType = ArrayType<MapType<StringType, ArrayType<bool>>>;
UserType array(1);
ArrayType<bool> map_value(2);
map_value[0] = false;
map_value[1] = true;
array[0].insert("hello world", std::move(map_value));
mojo::internal::SerializationContext context;
size_t size =
mojo::internal::PrepareToSerialize<MojomType>(array, &context);
mojo::internal::FixedBufferForTesting buf(size);
typename MojomType::Data_* data;
mojo::internal::ContainerValidateParams validate_params(
0, false,
new mojo::internal::ContainerValidateParams(
new mojo::internal::ContainerValidateParams(
0, false, new mojo::internal::ContainerValidateParams(
0, false, nullptr)),
new mojo::internal::ContainerValidateParams(
0, false, new mojo::internal::ContainerValidateParams(
0, false, nullptr))));
mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
&context);
UserType deserialized_array;
mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
&context);
ASSERT_EQ(1u, deserialized_array.size());
ASSERT_EQ(1u, deserialized_array[0].size());
ASSERT_FALSE(deserialized_array[0].at("hello world")[0]);
ASSERT_TRUE(deserialized_array[0].at("hello world")[1]);
}
}
};
#define MAP_COMMON_TEST(MapType, test_name) \
TEST_F(MapType##Test, test_name) { MapCommonTest<MapType>::test_name(); }
} // namespace test
} // namespace mojo
......@@ -9,12 +9,9 @@
#include <utility>
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
#include "mojo/public/cpp/bindings/lib/validate_params.h"
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/tests/container_test_util.h"
#include "mojo/public/cpp/bindings/tests/map_common_test.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
......@@ -22,97 +19,15 @@ namespace test {
namespace {
using mojo::internal::Array_Data;
using mojo::internal::ContainerValidateParams;
using mojo::internal::FixedBufferForTesting;
using mojo::internal::Map_Data;
using mojo::internal::String_Data;
struct StringIntData {
const char* string_data;
int int_data;
} kStringIntData[] = {
{"one", 1},
{"two", 2},
{"three", 3},
{"four", 4},
};
const size_t kStringIntDataSize = 4;
using MapTest = testing::Test;
// Tests null and empty maps.
TEST_F(MapTest, NullAndEmpty) {
Map<char, char> map0;
EXPECT_TRUE(map0.empty());
EXPECT_FALSE(map0.is_null());
map0 = nullptr;
EXPECT_TRUE(map0.is_null());
EXPECT_FALSE(map0.empty());
Map<char, char> map1(nullptr);
EXPECT_TRUE(map1.is_null());
EXPECT_FALSE(map1.empty());
map1.SetToEmpty();
EXPECT_TRUE(map1.empty());
EXPECT_FALSE(map1.is_null());
}
// Tests that basic Map operations work.
TEST_F(MapTest, InsertWorks) {
Map<String, int> map;
for (size_t i = 0; i < kStringIntDataSize; ++i)
map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
for (size_t i = 0; i < kStringIntDataSize; ++i) {
EXPECT_EQ(kStringIntData[i].int_data,
map.at(kStringIntData[i].string_data));
}
}
TEST_F(MapTest, TestIndexOperator) {
Map<String, int> map;
for (size_t i = 0; i < kStringIntDataSize; ++i)
map[kStringIntData[i].string_data] = kStringIntData[i].int_data;
for (size_t i = 0; i < kStringIntDataSize; ++i) {
EXPECT_EQ(kStringIntData[i].int_data,
map.at(kStringIntData[i].string_data));
}
}
TEST_F(MapTest, TestIndexOperatorAsRValue) {
Map<String, int> map;
for (size_t i = 0; i < kStringIntDataSize; ++i)
map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
for (size_t i = 0; i < kStringIntDataSize; ++i) {
EXPECT_EQ(kStringIntData[i].int_data, map[kStringIntData[i].string_data]);
}
}
TEST_F(MapTest, TestIndexOperatorMoveOnly) {
ASSERT_EQ(0u, MoveOnlyType::num_instances());
mojo::Map<mojo::String, mojo::Array<int32_t>> map;
std::vector<MoveOnlyType*> value_ptrs;
for (size_t i = 0; i < kStringIntDataSize; ++i) {
const char* key = kStringIntData[i].string_data;
Array<int32_t> array(1);
array[0] = kStringIntData[i].int_data;
map[key] = std::move(array);
EXPECT_TRUE(map);
}
// We now read back that data, to test the behavior of operator[].
for (size_t i = 0; i < kStringIntDataSize; ++i) {
auto it = map.find(kStringIntData[i].string_data);
ASSERT_TRUE(it != map.end());
ASSERT_EQ(1u, it->second.size());
EXPECT_EQ(kStringIntData[i].int_data, it->second[0]);
}
}
MAP_COMMON_TEST(Map, NullAndEmpty)
MAP_COMMON_TEST(Map, InsertWorks)
MAP_COMMON_TEST(Map, TestIndexOperator)
MAP_COMMON_TEST(Map, TestIndexOperatorAsRValue)
MAP_COMMON_TEST(Map, TestIndexOperatorMoveOnly)
MAP_COMMON_TEST(Map, MapArrayClone)
MAP_COMMON_TEST(Map, ArrayOfMap)
TEST_F(MapTest, ConstructedFromArray) {
Array<String> keys(kStringIntDataSize);
......@@ -272,83 +187,6 @@ TEST_F(MapTest, MojoToSTL) {
}
}
TEST_F(MapTest, MapArrayClone) {
Map<String, Array<String>> m;
for (size_t i = 0; i < kStringIntDataSize; ++i) {
Array<String> s;
s.push_back(kStringIntData[i].string_data);
m.insert(kStringIntData[i].string_data, std::move(s));
}
Map<String, Array<String>> m2 = m.Clone();
for (auto it = m2.begin(); it != m2.end(); ++it) {
ASSERT_EQ(1u, it->second.size());
EXPECT_EQ(it->first, it->second.at(0));
}
}
TEST_F(MapTest, ArrayOfMap) {
{
Array<Map<int32_t, int8_t>> array(1);
array[0].insert(1, 42);
mojo::internal::SerializationContext context;
size_t size =
mojo::internal::PrepareToSerialize<Array<Map<int32_t, int8_t>>>(
array, &context);
FixedBufferForTesting buf(size);
Array_Data<Map_Data<int32_t, int8_t>*>* data;
ContainerValidateParams validate_params(
0, false, new ContainerValidateParams(
new ContainerValidateParams(0, false, nullptr),
new ContainerValidateParams(0, false, nullptr)));
mojo::internal::Serialize<Array<Map<int32_t, int8_t>>>(
array, &buf, &data, &validate_params, &context);
Array<Map<int32_t, int8_t>> deserialized_array;
mojo::internal::Deserialize<Array<Map<int32_t, int8_t>>>(
data, &deserialized_array, &context);
ASSERT_EQ(1u, deserialized_array.size());
ASSERT_EQ(1u, deserialized_array[0].size());
ASSERT_EQ(42, deserialized_array[0].at(1));
}
{
Array<Map<String, Array<bool>>> array(1);
Array<bool> map_value(2);
map_value[0] = false;
map_value[1] = true;
array[0].insert("hello world", std::move(map_value));
mojo::internal::SerializationContext context;
size_t size =
mojo::internal::PrepareToSerialize<Array<Map<String, Array<bool>>>>(
array, &context);
FixedBufferForTesting buf(size);
Array_Data<Map_Data<String_Data*, Array_Data<bool>*>*>* data;
ContainerValidateParams validate_params(
0, false,
new ContainerValidateParams(
new ContainerValidateParams(
0, false, new ContainerValidateParams(0, false, nullptr)),
new ContainerValidateParams(
0, false, new ContainerValidateParams(0, false, nullptr))));
mojo::internal::Serialize<Array<Map<String, Array<bool>>>>(
array, &buf, &data, &validate_params, &context);
Array<Map<String, Array<bool>>> deserialized_array;
mojo::internal::Deserialize<Array<Map<String, Array<bool>>>>(
data, &deserialized_array, &context);
ASSERT_EQ(1u, deserialized_array.size());
ASSERT_EQ(1u, deserialized_array[0].size());
ASSERT_FALSE(deserialized_array[0].at("hello world")[0]);
ASSERT_TRUE(deserialized_array[0].at("hello world")[1]);
}
}
TEST_F(MapTest, MoveFromAndToSTLMap_Copyable) {
std::map<int32_t, CopyableType> map1;
map1.insert(std::make_pair(123, CopyableType()));
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/cpp/bindings/wtf_map.h"
#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
#include "mojo/public/cpp/bindings/tests/container_test_util.h"
#include "mojo/public/cpp/bindings/tests/map_common_test.h"
#include "mojo/public/cpp/bindings/wtf_array.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/Source/wtf/text/WTFString.h"
namespace mojo {
namespace test {
namespace {
using WTFMapTest = testing::Test;
MAP_COMMON_TEST(WTFMap, NullAndEmpty)
MAP_COMMON_TEST(WTFMap, InsertWorks)
MAP_COMMON_TEST(WTFMap, TestIndexOperator)
MAP_COMMON_TEST(WTFMap, TestIndexOperatorAsRValue)
MAP_COMMON_TEST(WTFMap, TestIndexOperatorMoveOnly)
MAP_COMMON_TEST(WTFMap, MapArrayClone)
MAP_COMMON_TEST(WTFMap, ArrayOfMap)
TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_Copyable) {
WTF::HashMap<int32_t, CopyableType> map1;
map1.add(123, CopyableType());
map1.find(123)->value.ResetCopied();
ASSERT_FALSE(map1.find(123)->value.copied());
WTFMap<int32_t, CopyableType> mojo_map(std::move(map1));
ASSERT_EQ(1u, mojo_map.size());
ASSERT_NE(mojo_map.end(), mojo_map.find(123));
ASSERT_FALSE(mojo_map[123].copied());
WTF::HashMap<int32_t, CopyableType> map2(mojo_map.PassStorage());
ASSERT_EQ(1u, map2.size());
ASSERT_NE(map2.end(), map2.find(123));
ASSERT_FALSE(map2.find(123)->value.copied());
ASSERT_EQ(0u, mojo_map.size());
ASSERT_TRUE(mojo_map.is_null());
}
TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_MoveOnly) {
WTF::HashMap<int32_t, MoveOnlyType> map1;
map1.add(123, MoveOnlyType());
WTFMap<int32_t, MoveOnlyType> mojo_map(std::move(map1));
ASSERT_EQ(1u, mojo_map.size());
ASSERT_NE(mojo_map.end(), mojo_map.find(123));
WTF::HashMap<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
ASSERT_EQ(1u, map2.size());
ASSERT_NE(map2.end(), map2.find(123));
ASSERT_EQ(0u, mojo_map.size());
ASSERT_TRUE(mojo_map.is_null());
}
} // namespace
} // namespace test
} // namespace mojo
......@@ -39,6 +39,11 @@ class TestWTFImpl : public TestWTF {
callback.Run(std::move(arr));
}
void EchoStringMap(Map<String, String> str_map,
const EchoStringMapCallback& callback) override {
callback.Run(std::move(str_map));
}
private:
Binding<TestWTF> binding_;
};
......@@ -62,6 +67,16 @@ WTFArray<WTF::String> ConstructStringArray() {
return strs;
}
WTFMap<WTF::String, WTF::String> ConstructStringMap() {
WTFMap<WTF::String, WTF::String> str_map;
// A null string as value.
str_map.insert("0", WTF::String());
str_map.insert("1", kHelloWorld);
str_map.insert("2", WTF::String::fromUTF8(kUTF8HelloWorld));
return str_map;
}
} // namespace
TEST_F(WTFTypesTest, Serialization_WTFArrayToWTFArray) {
......@@ -80,7 +95,7 @@ TEST_F(WTFTypesTest, Serialization_WTFArrayToWTFArray) {
&validate_params, &context);
WTFArray<WTF::String> strs2;
mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, nullptr);
mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
EXPECT_TRUE(strs.Equals(strs2));
}
......@@ -101,7 +116,7 @@ TEST_F(WTFTypesTest, Serialization_WTFVectorToWTFVector) {
&validate_params, &context);
WTF::Vector<WTF::String> strs2;
mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, nullptr);
mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
EXPECT_EQ(strs, strs2);
}
......@@ -121,7 +136,7 @@ TEST_F(WTFTypesTest, Serialization_WTFArrayToMojoArray) {
&validate_params, &context);
Array<mojo::String> strs2;
mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, nullptr);
mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
ASSERT_EQ(4u, strs2.size());
EXPECT_TRUE(strs2[0].is_null());
......@@ -130,6 +145,66 @@ TEST_F(WTFTypesTest, Serialization_WTFArrayToMojoArray) {
EXPECT_TRUE(kUTF8HelloWorld == strs2[3]);
}
TEST_F(WTFTypesTest, Serialization_WTFMapToWTFMap) {
using WTFType = WTFMap<WTF::String, WTF::String>;
using MojomType = Map<mojo::String, mojo::String>;
WTFType str_map = ConstructStringMap();
WTFType cloned_str_map = str_map.Clone();
mojo::internal::SerializationContext context;
size_t size =
mojo::internal::PrepareToSerialize<MojomType>(cloned_str_map, &context);
mojo::internal::FixedBufferForTesting buf(size);
typename MojomType::Data_* data;
mojo::internal::ContainerValidateParams validate_params(
new mojo::internal::ContainerValidateParams(
0, false,
new mojo::internal::ContainerValidateParams(0, false, nullptr)),
new mojo::internal::ContainerValidateParams(
0, true,
new mojo::internal::ContainerValidateParams(0, false, nullptr)));
mojo::internal::Serialize<MojomType>(cloned_str_map, &buf, &data,
&validate_params, &context);
WTFType str_map2;
mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
EXPECT_TRUE(str_map.Equals(str_map2));
}
TEST_F(WTFTypesTest, Serialization_WTFMapToMojoMap) {
using WTFType = WTFMap<WTF::String, WTF::String>;
using MojomType = Map<mojo::String, mojo::String>;
WTFType str_map = ConstructStringMap();
mojo::internal::SerializationContext context;
size_t size =
mojo::internal::PrepareToSerialize<MojomType>(str_map, &context);
mojo::internal::FixedBufferForTesting buf(size);
typename MojomType::Data_* data;
mojo::internal::ContainerValidateParams validate_params(
new mojo::internal::ContainerValidateParams(
0, false,
new mojo::internal::ContainerValidateParams(0, false, nullptr)),
new mojo::internal::ContainerValidateParams(
0, true,
new mojo::internal::ContainerValidateParams(0, false, nullptr)));
mojo::internal::Serialize<MojomType>(str_map, &buf, &data, &validate_params,
&context);
MojomType str_map2;
mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
ASSERT_EQ(3u, str_map2.size());
EXPECT_TRUE(str_map2["0"].is_null());
EXPECT_TRUE(kHelloWorld == str_map2["1"]);
EXPECT_TRUE(kUTF8HelloWorld == str_map2["2"]);
}
TEST_F(WTFTypesTest, SendString) {
blink::TestWTFPtr ptr;
TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
......@@ -171,7 +246,7 @@ TEST_F(WTFTypesTest, SendStringArray) {
// - serialized;
// - deserialized as mojo::WTFArray<WTF::String>.
ptr->EchoStringArray(std::move(arrs[i]),
[&loop, &expected_arr, &i](WTFArray<WTF::String> arr) {
[&loop, &expected_arr](WTFArray<WTF::String> arr) {
EXPECT_TRUE(expected_arr.Equals(arr));
loop.Quit();
});
......@@ -179,5 +254,34 @@ TEST_F(WTFTypesTest, SendStringArray) {
}
}
TEST_F(WTFTypesTest, SendStringMap) {
blink::TestWTFPtr ptr;
TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
WTFMap<WTF::String, WTF::String> maps[3];
// maps[0] is empty.
// maps[1] is null.
maps[1] = nullptr;
maps[2] = ConstructStringMap();
for (size_t i = 0; i < arraysize(maps); ++i) {
WTFMap<WTF::String, WTF::String> expected_map = maps[i].Clone();
base::RunLoop loop;
// Test that a mojo::WTFMap<WTF::String, WTF::String> is unchanged after the
// following conversion:
// - serialized;
// - deserialized as mojo::Map<mojo::String, mojo::String>;
// - serialized;
// - deserialized as mojo::WTFMap<WTF::String, WTF::String>.
ptr->EchoStringMap(
std::move(maps[i]),
[&loop, &expected_map](WTFMap<WTF::String, WTF::String> map) {
EXPECT_TRUE(expected_map.Equals(map));
loop.Quit();
});
loop.Run();
}
}
} // namespace test
} // namespace mojo
......@@ -26,10 +26,6 @@ namespace mojo {
template <typename T>
class WTFArray {
public:
using Data_ = internal::Array_Data<
typename internal::GetDataTypeAsArrayElement<T>::Data>;
using Element = T;
// Constructs an empty array.
WTFArray() : is_null_(false) {}
// Constructs a null array.
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
#define MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
#include <stddef.h>
#include <utility>
#include "base/macros.h"
#include "mojo/public/cpp/bindings/lib/template_util.h"
#include "mojo/public/cpp/bindings/type_converter.h"
#include "third_party/WebKit/Source/wtf/HashMap.h"
#include "third_party/WebKit/Source/wtf/text/StringHash.h"
namespace mojo {
// Represents a map backed by WTF::HashMap. Comparing with WTF::HashMap,
// mojo::WTFMap is move-only and can be null.
//
// It is easy to convert between WTF::HashMap<K, V> and mojo::WTFMap<K, V>:
// - constructor WTFMap(WTF::HashMap<K, V>&&) takes the contents of a
// WTF::HashMap<K, V>;
// - method PassStorage() passes the underlying WTF::HashMap.
//
// NOTE: WTF::HashMap disallows certain key values. For integer types, those are
// 0 and -1 (max value instead of -1 for unsigned). For string, that is null.
template <typename Key, typename Value>
class WTFMap {
public:
using Iterator = typename WTF::HashMap<Key, Value>::iterator;
using ConstIterator = typename WTF::HashMap<Key, Value>::const_iterator;
// Constructs an empty map.
WTFMap() : is_null_(false) {}
// Constructs a null map.
WTFMap(std::nullptr_t null_pointer) : is_null_(true) {}
~WTFMap() {}
WTFMap(WTF::HashMap<Key, Value>&& other)
: map_(std::move(other)), is_null_(false) {}
WTFMap(WTFMap&& other) : is_null_(true) { Take(&other); }
WTFMap& operator=(WTF::HashMap<Key, Value>&& other) {
is_null_ = false;
map_ = std::move(other);
return *this;
}
WTFMap& operator=(WTFMap&& other) {
Take(&other);
return *this;
}
WTFMap& operator=(std::nullptr_t null_pointer) {
is_null_ = true;
map_.clear();
return *this;
}
static bool IsValidKey(const Key& key) {
return WTF::HashMap<Key, Value>::isValidKey(key);
}
// Copies the contents of some other type of map into a new WTFMap using a
// TypeConverter.
template <typename U>
static WTFMap From(const U& other) {
return TypeConverter<WTFMap, U>::Convert(other);
}
// Copies the contents of the WTFMap into some other type of map.
template <typename U>
U To() const {
return TypeConverter<U, WTFMap>::Convert(*this);
}
// Indicates whether the map is null (which is distinct from empty).
bool is_null() const { return is_null_; }
// Indicates whether the map is empty (which is distinct from null).
bool empty() const { return map_.isEmpty() && !is_null_; }
// Indicates the number of keys in the map, which will be zero if the map is
// null.
size_t size() const { return map_.size(); }
// Inserts a key-value pair into the map. Like WTF::HashMap::add(), this does
// not insert |value| if |key| is already a member of the map.
void insert(const Key& key, const Value& value) {
is_null_ = false;
map_.add(key, value);
}
void insert(const Key& key, Value&& value) {
is_null_ = false;
map_.add(key, std::move(value));
}
// Returns a reference to the value associated with the specified key,
// crashing the process if the key is not present in the map.
Value& at(const Key& key) { return map_.find(key)->value; }
const Value& at(const Key& key) const { return map_.find(key)->value; }
// Returns a reference to the value associated with the specified key,
// creating a new entry if the key is not already present in the map. A
// newly-created value will be value-initialized (meaning that it will be
// initialized by the default constructor of the value type, if any, or else
// will be zero-initialized).
Value& operator[](const Key& key) {
is_null_ = false;
if (!map_.contains(key))
map_.add(key, Value());
return at(key);
}
// Sets the map to empty (even if previously it was null).
void SetToEmpty() {
is_null_ = false;
map_.clear();
}
// Returns a const reference to the WTF::HashMap managed by this class. If
// this object is null, the return value will be an empty map.
const WTF::HashMap<Key, Value>& storage() const { return map_; }
// Passes the underlying storage and resets this map to null.
WTF::HashMap<Key, Value> PassStorage() {
is_null_ = true;
return std::move(map_);
}
// Swaps the contents of this WTFMap with another WTFMap of the same type
// (including nullness).
void Swap(WTFMap<Key, Value>* other) {
std::swap(is_null_, other->is_null_);
map_.swap(other->map_);
}
// Swaps the contents of this WTFMap with an WTF::HashMap containing keys and
// values of the same type. Since WTF::HashMap cannot represent the null
// state, the WTF::HashMap will be empty if WTFMap is null. The WTFMap will
// always be left in a non-null state.
void Swap(WTF::HashMap<Key, Value>* other) {
is_null_ = false;
map_.swap(*other);
}
// Returns a new WTFMap that contains a copy of the contents of this map. If
// the key/value type defines a Clone() method, it will be used; otherwise
// copy constructor/assignment will be used.
//
// Please note that calling this method will fail compilation if the key/value
// type cannot be cloned (which usually means that it is a Mojo handle type or
// a type containing Mojo handles).
WTFMap Clone() const {
WTFMap result;
result.is_null_ = is_null_;
auto map_end = map_.end();
for (auto it = map_.begin(); it != map_end; ++it)
result.map_.add(internal::Clone(it->key), internal::Clone(it->value));
return result;
}
// Indicates whether the contents of this map are equal to those of another
// WTFMap (including nullness). If the key/value type defines an Equals()
// method, it will be used; otherwise == operator will be used.
bool Equals(const WTFMap& other) const {
if (is_null() != other.is_null())
return false;
if (size() != other.size())
return false;
auto this_end = map_.end();
auto other_end = other.map_.end();
for (auto iter = map_.begin(); iter != this_end; ++iter) {
auto other_iter = other.map_.find(iter->key);
if (other_iter == other_end ||
!internal::Equals(iter->value, other_iter->value)) {
return false;
}
}
return true;
}
ConstIterator begin() const { return map_.begin(); }
Iterator begin() { return map_.begin(); }
ConstIterator end() const { return map_.end(); }
Iterator end() { return map_.end(); }
// Returns the iterator pointing to the entry for |key|, if present, or else
// returns end().
ConstIterator find(const Key& key) const { return map_.find(key); }
Iterator find(const Key& key) { return map_.find(key); }
explicit operator bool() const { return !is_null_; }
private:
void Take(WTFMap* other) {
operator=(nullptr);
Swap(other);
}
WTF::HashMap<Key, Value> map_;
bool is_null_;
DISALLOW_COPY_AND_ASSIGN(WTFMap);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
......@@ -113,7 +113,8 @@ struct ScopedConstants {
// decoded successfully.
struct MapKeyTypes {
map<bool, bool> f0;
// TODO(yzshen): WTF::HashMap doesn't support bool as key.
// map<bool, bool> f0;
map<int8, int8> f1;
map<uint8, uint8> f2;
map<int16, int16> f3;
......
......@@ -12,14 +12,21 @@ struct TestWTFCodeGeneration {
array<array<int32>> arrays;
array<bool> bools;
array<handle<message_pipe>> handles;
map<string, string?> str_map;
map<int32, array<int32>> array_map;
map<int32, handle<message_pipe>> handle_map;
array<map<string, string?>> str_maps;
};
union TestWTFCodeGeneration2 {
string str;
array<string> strs;
map<string, string?> str_map;
};
interface TestWTF {
EchoString(string? str) => (string? str);
EchoStringArray(array<string?>? arr) => (array<string?>? arr);
EchoStringMap(map<string, string?>? str_map)
=> (map<string, string?>? str_map);
};
......@@ -24,15 +24,6 @@ foreach(typemap, _typemaps) {
blacklist = [
# TODO(sammc): Remove the following once |for_blink| bindings support WTF
# maps. See https://crbug.com/583738.
"//components/mus/public/interfaces/clipboard.mojom",
"//components/mus/public/interfaces/window_manager.mojom",
"//components/mus/public/interfaces/window_tree.mojom",
"//extensions/common/api/mime_handler.mojom",
"//mojo/public/interfaces/bindings/tests/struct_with_traits.mojom",
"//mojo/public/interfaces/bindings/tests/test_structs.mojom",
"//mojo/public/interfaces/bindings/tests/test_unions.mojom",
# maps with enum keys. See https://crbug.com/583738.
"//mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
"//services/catalog/public/interfaces/catalog.mojom",
"//services/shell/public/interfaces/capabilities.mojom",
]
......@@ -50,6 +50,7 @@
#include "mojo/public/cpp/bindings/string.h"
{%- else %}
#include "mojo/public/cpp/bindings/wtf_array.h"
#include "mojo/public/cpp/bindings/wtf_map.h"
#include "third_party/WebKit/Source/wtf/text/WTFString.h"
{%- endif %}
......
......@@ -184,8 +184,9 @@ def GetCppArrayArgWrapperType(kind):
pattern = "mojo::WTFArray<%s>" if _for_blink else "mojo::Array<%s>"
return pattern % GetCppArrayArgWrapperType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::Map<%s, %s> " % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
pattern = "mojo::WTFMap<%s, %s>" if _for_blink else "mojo::Map<%s, %s>"
return pattern % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
raise Exception("Arrays of interfaces not yet supported!")
if mojom.IsInterfaceRequestKind(kind):
......@@ -220,8 +221,9 @@ def GetCppWrapperType(kind):
pattern = "mojo::WTFArray<%s>" if _for_blink else "mojo::Array<%s>"
return pattern % GetCppArrayArgWrapperType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
pattern = "mojo::WTFMap<%s, %s>" if _for_blink else "mojo::Map<%s, %s>"
return pattern % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsInterfaceRequestKind(kind):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment