diff options
author | yzshen <yzshen@chromium.org> | 2016-03-15 11:43:53 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-15 18:46:37 +0000 |
commit | 583ed9487b207982774fcfa11c563b7add77a808 (patch) | |
tree | 643ec1bf9e4bb6800bb5bf629640280e097f168a | |
parent | 37b82939f4a1dca844292eb73874bfd8175c7754 (diff) | |
download | chromium_src-583ed9487b207982774fcfa11c563b7add77a808.zip chromium_src-583ed9487b207982774fcfa11c563b7add77a808.tar.gz chromium_src-583ed9487b207982774fcfa11c563b7add77a808.tar.bz2 |
Mojo C++ bindings: introduce mojo::WTFArray.
mojo::WTFArray is a thin wrapper around WTF::Vector. It is move-only and can express null array. You can move WTF::Vector in/out of mojo::WTFArray.
If "--for_blink" is specified for the bindings generator, the generator will map mojo array to mojo::WTFArray
BUG=583738
Review URL: https://codereview.chromium.org/1766093002
Cr-Commit-Position: refs/heads/master@{#381265}
20 files changed, 872 insertions, 413 deletions
diff --git a/mojo/mojo_edk_tests.gyp b/mojo/mojo_edk_tests.gyp index 539a161..01d01c4 100644 --- a/mojo/mojo_edk_tests.gyp +++ b/mojo/mojo_edk_tests.gyp @@ -57,6 +57,7 @@ 'mojo_public.gyp:mojo_public_test_utils', ], 'sources': [ + 'public/cpp/bindings/tests/array_common_test.h', 'public/cpp/bindings/tests/array_unittest.cc', 'public/cpp/bindings/tests/associated_interface_unittest.cc', 'public/cpp/bindings/tests/binding_callback_unittest.cc', @@ -67,6 +68,7 @@ 'public/cpp/bindings/tests/connector_unittest.cc', 'public/cpp/bindings/tests/constant_unittest.cc', 'public/cpp/bindings/tests/container_test_util.cc', + 'public/cpp/bindings/tests/container_test_util.h', 'public/cpp/bindings/tests/equals_unittest.cc', 'public/cpp/bindings/tests/handle_passing_unittest.cc', 'public/cpp/bindings/tests/interface_ptr_unittest.cc', @@ -98,6 +100,7 @@ 'public/cpp/bindings/tests/type_conversion_unittest.cc', 'public/cpp/bindings/tests/union_unittest.cc', 'public/cpp/bindings/tests/validation_unittest.cc', + 'public/cpp/bindings/tests/variant_test_util.h', ], }, { @@ -107,10 +110,16 @@ 'dependencies': [ '../testing/gtest.gyp:gtest', 'mojo_public.gyp:mojo_cpp_bindings', + 'mojo_public.gyp:mojo_public_test_interfaces', 'mojo_public.gyp:mojo_public_test_wtf_types', 'mojo_public.gyp:mojo_public_test_wtf_types_blink', ], 'sources': [ + '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/variant_test_util.h', + 'public/cpp/bindings/tests/wtf_array_unittest.cc', 'public/cpp/bindings/tests/wtf_types_unittest.cc', ], }, diff --git a/mojo/mojo_public.gyp b/mojo/mojo_public.gyp index da318ae..bb12a5b 100644 --- a/mojo/mojo_public.gyp +++ b/mojo/mojo_public.gyp @@ -204,9 +204,11 @@ '..' ], 'sources': [ + 'public/cpp/bindings/lib/wtf_array_serialization.h', 'public/cpp/bindings/lib/wtf_serialization.h', 'public/cpp/bindings/lib/wtf_string_serialization.cc', 'public/cpp/bindings/lib/wtf_string_serialization.h', + 'public/cpp/bindings/wtf_array.h', ], 'dependencies': [ 'mojo_cpp_bindings', diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn index 1939ff6..b817de7 100644 --- a/mojo/public/cpp/bindings/BUILD.gn +++ b/mojo/public/cpp/bindings/BUILD.gn @@ -127,9 +127,11 @@ source_set("callback") { source_set("wtf_support") { sources = [ + "lib/wtf_array_serialization.h", "lib/wtf_serialization.h", "lib/wtf_string_serialization.cc", "lib/wtf_string_serialization.h", + "wtf_array.h", ] public_deps = [ diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h index 575ba77..f522faa 100644 --- a/mojo/public/cpp/bindings/array.h +++ b/mojo/public/cpp/bindings/array.h @@ -13,6 +13,7 @@ #include <utility> #include <vector> +#include "base/move.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" #include "mojo/public/cpp/bindings/lib/template_util.h" @@ -221,7 +222,7 @@ class Array { struct CloneTraits<U, true> { static inline void Clone(const std::vector<T>& src_vec, std::vector<T>* dest_vec) { - dest_vec->clear(); + DCHECK(dest_vec->empty()); dest_vec->reserve(src_vec.size()); for (const auto& element : src_vec) dest_vec->push_back(element.Clone()); diff --git a/mojo/public/cpp/bindings/lib/array_serialization_traits.h b/mojo/public/cpp/bindings/lib/array_serialization_traits.h index 35b12f1..5e0e5af 100644 --- a/mojo/public/cpp/bindings/lib/array_serialization_traits.h +++ b/mojo/public/cpp/bindings/lib/array_serialization_traits.h @@ -194,7 +194,8 @@ struct ArraySerializer< private: template <typename T, - bool is_array = IsSpecializationOf<Array, T>::value, + bool is_array = IsSpecializationOf<Array, T>::value || + IsSpecializationOf<WTFArray, T>::value, bool is_string = std::is_same<T, String>::value || std::is_same<T, WTF::String>::value> struct SerializeCaller { diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc index 065c176..55263a9 100644 --- a/mojo/public/cpp/bindings/lib/connector.cc +++ b/mojo/public/cpp/bindings/lib/connector.cc @@ -287,8 +287,8 @@ void Connector::WaitToReadMore() { // If the watch failed because the handle is invalid or its conditions can // no longer be met, we signal the error asynchronously to avoid reentry. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&Connector::OnWatcherHandleReady, - weak_self_, rv)); + FROM_HERE, + base::Bind(&Connector::OnWatcherHandleReady, weak_self_, rv)); } if (register_sync_handle_watch_count_ > 0 && diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h index 39d046c..b605049 100644 --- a/mojo/public/cpp/bindings/lib/serialization_forward.h +++ b/mojo/public/cpp/bindings/lib/serialization_forward.h @@ -23,6 +23,9 @@ class Array; template <typename K, typename V> class Map; +template <typename T> +class WTFArray; + namespace internal { template <typename T> @@ -80,6 +83,26 @@ inline bool Deserialize_(internal::Array_Data<F>* input, internal::SerializationContext* context); // ----------------------------------------------------------------------------- +// Forward declaration for WTFArray. + +template <typename E> +inline size_t GetSerializedSize_(const WTFArray<E>& input, + internal::SerializationContext* context); + +template <typename E, typename F> +inline void SerializeArray_( + WTFArray<E> input, + internal::Buffer* buf, + internal::Array_Data<F>** output, + const internal::ArrayValidateParams* validate_params, + internal::SerializationContext* context); + +template <typename E, typename F> +inline bool Deserialize_(internal::Array_Data<F>* input, + WTFArray<E>* output, + internal::SerializationContext* context); + +// ----------------------------------------------------------------------------- // Forward declaration for Map. template <typename MapKey, typename MapValue> diff --git a/mojo/public/cpp/bindings/lib/value_traits.h b/mojo/public/cpp/bindings/lib/value_traits.h index d3b295b..56bdf3a 100644 --- a/mojo/public/cpp/bindings/lib/value_traits.h +++ b/mojo/public/cpp/bindings/lib/value_traits.h @@ -36,6 +36,9 @@ class ScopedHandleBase; template <typename T> class StructPtr; +template <typename T> +class WTFArray; + namespace internal { template <typename T, typename Enable = void> @@ -47,6 +50,7 @@ template <typename T> struct ValueTraits< T, typename EnableIf<IsSpecializationOf<Array, T>::value || + IsSpecializationOf<WTFArray, T>::value || IsSpecializationOf<Map, T>::value || IsSpecializationOf<StructPtr, T>::value || IsSpecializationOf<InlinedStructPtr, T>::value>::type> { diff --git a/mojo/public/cpp/bindings/lib/wtf_array_serialization.h b/mojo/public/cpp/bindings/lib/wtf_array_serialization.h new file mode 100644 index 0000000..70edbf1 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/wtf_array_serialization.h @@ -0,0 +1,43 @@ +// 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_LIB_WTF_ARRAY_SERIALIZATION_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_ARRAY_SERIALIZATION_H_ + +#include <stddef.h> + +#include "mojo/public/cpp/bindings/lib/array_serialization_traits.h" +#include "mojo/public/cpp/bindings/wtf_array.h" + +namespace mojo { + +template <typename E> +inline size_t GetSerializedSize_(const WTFArray<E>& input, + internal::SerializationContext* context) { + return internal::ArraySerializationImpl<WTFArray<E>>::GetSerializedSize( + input, context); +} + +template <typename E, typename F> +inline void SerializeArray_( + WTFArray<E> input, + internal::Buffer* buf, + internal::Array_Data<F>** output, + const internal::ArrayValidateParams* validate_params, + internal::SerializationContext* context) { + return internal::ArraySerializationImpl<WTFArray<E>>::template Serialize<F>( + std::move(input), buf, output, validate_params, context); +} + +template <typename E, typename F> +inline bool Deserialize_(internal::Array_Data<F>* input, + WTFArray<E>* output, + internal::SerializationContext* context) { + return internal::ArraySerializationImpl<WTFArray<E>>::template Deserialize<F>( + input, output, context); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_ARRAY_SERIALIZATION_H_ diff --git a/mojo/public/cpp/bindings/lib/wtf_serialization.h b/mojo/public/cpp/bindings/lib/wtf_serialization.h index 11733ec..318d152 100644 --- a/mojo/public/cpp/bindings/lib/wtf_serialization.h +++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_ #define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_ +#include "mojo/public/cpp/bindings/lib/wtf_array_serialization.h" #include "mojo/public/cpp/bindings/lib/wtf_string_serialization.h" #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_ diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn index 7bbd0bd..043ed74 100644 --- a/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/mojo/public/cpp/bindings/tests/BUILD.gn @@ -8,6 +8,7 @@ source_set("tests") { testonly = true sources = [ + "array_common_test.h", "array_unittest.cc", "associated_interface_unittest.cc", "binding_callback_unittest.cc", @@ -18,6 +19,7 @@ source_set("tests") { "connector_unittest.cc", "constant_unittest.cc", "container_test_util.cc", + "container_test_util.h", "equals_unittest.cc", "handle_passing_unittest.cc", "interface_ptr_unittest.cc", @@ -49,6 +51,7 @@ source_set("tests") { "type_conversion_unittest.cc", "union_unittest.cc", "validation_unittest.cc", + "variant_test_util.h", ] deps = [ @@ -72,6 +75,11 @@ source_set("for_blink_tests") { testonly = true sources = [ + "array_common_test.h", + "container_test_util.cc", + "container_test_util.h", + "variant_test_util.h", + "wtf_array_unittest.cc", "wtf_types_unittest.cc", ] @@ -79,6 +87,7 @@ source_set("for_blink_tests") { "//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings:callback", "//mojo/public/cpp/system", + "//mojo/public/interfaces/bindings/tests:test_interfaces", "//mojo/public/interfaces/bindings/tests:test_wtf_types", "//mojo/public/interfaces/bindings/tests:test_wtf_types_blink", "//testing/gtest", diff --git a/mojo/public/cpp/bindings/tests/array_common_test.h b/mojo/public/cpp/bindings/tests/array_common_test.h new file mode 100644 index 0000000..19c0f6f --- /dev/null +++ b/mojo/public/cpp/bindings/tests/array_common_test.h @@ -0,0 +1,392 @@ +// 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 <stddef.h> +#include <stdint.h> +#include <utility> + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/tests/container_test_util.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { + +// Common tests for both mojo::Array and mojo::WTFArray. +template <template <typename...> class ArrayType> +class ArrayCommonTest { + public: + // Tests null and empty arrays. + static void NullAndEmpty() { + ArrayType<char> array0; + EXPECT_TRUE(array0.empty()); + EXPECT_FALSE(array0.is_null()); + array0 = nullptr; + EXPECT_TRUE(array0.is_null()); + EXPECT_FALSE(array0.empty()); + + ArrayType<char> array1(nullptr); + EXPECT_TRUE(array1.is_null()); + EXPECT_FALSE(array1.empty()); + array1.SetToEmpty(); + EXPECT_TRUE(array1.empty()); + EXPECT_FALSE(array1.is_null()); + } + + // Tests that basic array operations work. + static void Basic() { + ArrayType<char> array(8); + for (size_t i = 0; i < array.size(); ++i) { + char val = static_cast<char>(i * 2); + array[i] = val; + EXPECT_EQ(val, array.at(i)); + } + } + + // Tests that basic ArrayType<bool> operations work. + static void Bool() { + ArrayType<bool> array(64); + for (size_t i = 0; i < array.size(); ++i) { + bool val = i % 3 == 0; + array[i] = val; + EXPECT_EQ(val, array.at(i)); + } + } + + // Tests that ArrayType<ScopedMessagePipeHandle> supports transferring + // handles. + static void Handle() { + MessagePipe pipe; + ArrayType<ScopedMessagePipeHandle> handles(2); + handles[0] = std::move(pipe.handle0); + handles[1].reset(pipe.handle1.release()); + + EXPECT_FALSE(pipe.handle0.is_valid()); + EXPECT_FALSE(pipe.handle1.is_valid()); + + ArrayType<ScopedMessagePipeHandle> handles2 = std::move(handles); + EXPECT_TRUE(handles2[0].is_valid()); + EXPECT_TRUE(handles2[1].is_valid()); + + ScopedMessagePipeHandle pipe_handle = std::move(handles2[0]); + EXPECT_TRUE(pipe_handle.is_valid()); + EXPECT_FALSE(handles2[0].is_valid()); + } + + // Tests that ArrayType<ScopedMessagePipeHandle> supports closing handles. + static void HandlesAreClosed() { + MessagePipe pipe; + MojoHandle pipe0_value = pipe.handle0.get().value(); + MojoHandle pipe1_value = pipe.handle0.get().value(); + + { + ArrayType<ScopedMessagePipeHandle> handles(2); + handles[0] = std::move(pipe.handle0); + handles[1].reset(pipe.handle0.release()); + } + + // We expect the pipes to have been closed. + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe0_value)); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe1_value)); + } + + static void Clone() { + { + // Test POD. + ArrayType<int32_t> array(3); + for (size_t i = 0; i < array.size(); ++i) + array[i] = static_cast<int32_t>(i); + + ArrayType<int32_t> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + for (size_t i = 0; i < array.size(); ++i) + EXPECT_EQ(array[i], clone_array[i]); + } + + { + // Test copyable object. + ArrayType<String> array(2); + array[0] = "hello"; + array[1] = "world"; + + ArrayType<String> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + for (size_t i = 0; i < array.size(); ++i) + EXPECT_EQ(array[i], clone_array[i]); + } + + { + // Test struct. + ArrayType<RectPtr> array(2); + array[1] = Rect::New(); + array[1]->x = 1; + array[1]->y = 2; + array[1]->width = 3; + array[1]->height = 4; + + ArrayType<RectPtr> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + EXPECT_TRUE(clone_array[0].is_null()); + EXPECT_EQ(array[1]->x, clone_array[1]->x); + EXPECT_EQ(array[1]->y, clone_array[1]->y); + EXPECT_EQ(array[1]->width, clone_array[1]->width); + EXPECT_EQ(array[1]->height, clone_array[1]->height); + } + + { + // Test array of array. + ArrayType<ArrayType<int8_t>> array(2); + array[0] = nullptr; + array[1] = ArrayType<int8_t>(2); + array[1][0] = 0; + array[1][1] = 1; + + ArrayType<ArrayType<int8_t>> clone_array = array.Clone(); + EXPECT_EQ(array.size(), clone_array.size()); + EXPECT_TRUE(clone_array[0].is_null()); + EXPECT_EQ(array[1].size(), clone_array[1].size()); + EXPECT_EQ(array[1][0], clone_array[1][0]); + EXPECT_EQ(array[1][1], clone_array[1][1]); + } + + { + // Test that array of handles still works although Clone() is not + // available. + ArrayType<ScopedMessagePipeHandle> array(10); + EXPECT_FALSE(array[0].is_valid()); + } + } + + static void Serialization_ArrayOfPOD() { + ArrayType<int32_t> array(4); + for (size_t i = 0; i < array.size(); ++i) + array[i] = static_cast<int32_t>(i); + + size_t size = GetSerializedSize_(array, nullptr); + EXPECT_EQ(8U + 4 * 4U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<int32_t>* data; + mojo::internal::ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); + + ArrayType<int32_t> array2; + Deserialize_(data, &array2, nullptr); + + EXPECT_EQ(4U, array2.size()); + for (size_t i = 0; i < array2.size(); ++i) + EXPECT_EQ(static_cast<int32_t>(i), array2[i]); + } + + static void Serialization_EmptyArrayOfPOD() { + ArrayType<int32_t> array; + size_t size = GetSerializedSize_(array, nullptr); + EXPECT_EQ(8U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<int32_t>* data; + mojo::internal::ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); + + ArrayType<int32_t> array2; + Deserialize_(data, &array2, nullptr); + EXPECT_EQ(0U, array2.size()); + } + + static void Serialization_ArrayOfArrayOfPOD() { + ArrayType<ArrayType<int32_t>> array(2); + for (size_t j = 0; j < array.size(); ++j) { + ArrayType<int32_t> inner(4); + for (size_t i = 0; i < inner.size(); ++i) + inner[i] = static_cast<int32_t>(i + (j * 10)); + array[j] = std::move(inner); + } + + size_t size = GetSerializedSize_(array, nullptr); + EXPECT_EQ(8U + 2 * 8U + 2 * (8U + 4 * 4U), size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<mojo::internal::Array_Data<int32_t>*>* data; + mojo::internal::ArrayValidateParams validate_params( + 0, false, new mojo::internal::ArrayValidateParams(0, false, nullptr)); + SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); + + ArrayType<ArrayType<int32_t>> array2; + Deserialize_(data, &array2, nullptr); + + EXPECT_EQ(2U, array2.size()); + for (size_t j = 0; j < array2.size(); ++j) { + const ArrayType<int32_t>& inner = array2[j]; + EXPECT_EQ(4U, inner.size()); + for (size_t i = 0; i < inner.size(); ++i) + EXPECT_EQ(static_cast<int32_t>(i + (j * 10)), inner[i]); + } + } + + static void Serialization_ArrayOfBool() { + ArrayType<bool> array(10); + for (size_t i = 0; i < array.size(); ++i) + array[i] = i % 2 ? true : false; + + size_t size = GetSerializedSize_(array, nullptr); + EXPECT_EQ(8U + 8U, size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<bool>* data; + mojo::internal::ArrayValidateParams validate_params(0, false, nullptr); + SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); + + ArrayType<bool> array2; + Deserialize_(data, &array2, nullptr); + + EXPECT_EQ(10U, array2.size()); + for (size_t i = 0; i < array2.size(); ++i) + EXPECT_EQ(i % 2 ? true : false, array2[i]); + } + + static void Serialization_ArrayOfString() { + ArrayType<String> array(10); + for (size_t i = 0; i < array.size(); ++i) { + char c = 'A' + static_cast<char>(i); + array[i] = String(&c, 1); + } + + size_t size = GetSerializedSize_(array, nullptr); + EXPECT_EQ(8U + // array header + 10 * 8U + // array payload (10 pointers) + 10 * (8U + // string header + 8U), // string length of 1 padded to 8 + size); + + mojo::internal::FixedBufferForTesting buf(size); + mojo::internal::Array_Data<mojo::internal::String_Data*>* data; + mojo::internal::ArrayValidateParams validate_params( + 0, false, new mojo::internal::ArrayValidateParams(0, false, nullptr)); + SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); + + ArrayType<String> array2; + Deserialize_(data, &array2, nullptr); + + EXPECT_EQ(10U, array2.size()); + for (size_t i = 0; i < array2.size(); ++i) { + char c = 'A' + static_cast<char>(i); + EXPECT_EQ(String(&c, 1), array2[i]); + } + } + + static void Resize_Copyable() { + ASSERT_EQ(0u, CopyableType::num_instances()); + ArrayType<CopyableType> array(3); + std::vector<CopyableType*> value_ptrs; + value_ptrs.push_back(array[0].ptr()); + value_ptrs.push_back(array[1].ptr()); + + for (size_t i = 0; i < array.size(); i++) + array[i].ResetCopied(); + + array.resize(2); + ASSERT_EQ(2u, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < array.size(); i++) { + EXPECT_FALSE(array[i].copied()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + + array.resize(3); + array[2].ResetCopied(); + ASSERT_EQ(3u, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].copied()); + value_ptrs.push_back(array[2].ptr()); + + size_t capacity = array.storage().capacity(); + array.resize(capacity); + ASSERT_EQ(capacity, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < 3; i++) + EXPECT_FALSE(array[i].copied()); + for (size_t i = 3; i < array.size(); i++) { + array[i].ResetCopied(); + value_ptrs.push_back(array[i].ptr()); + } + + array.resize(capacity + 2); + ASSERT_EQ(capacity + 2, array.size()); + EXPECT_EQ(array.size(), CopyableType::num_instances()); + for (size_t i = 0; i < capacity; i++) { + EXPECT_TRUE(array[i].copied()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + array = nullptr; + EXPECT_EQ(0u, CopyableType::num_instances()); + EXPECT_FALSE(array); + array.resize(0); + EXPECT_EQ(0u, CopyableType::num_instances()); + EXPECT_TRUE(array); + } + + static void Resize_MoveOnly() { + ASSERT_EQ(0u, MoveOnlyType::num_instances()); + ArrayType<MoveOnlyType> array(3); + std::vector<MoveOnlyType*> value_ptrs; + value_ptrs.push_back(array[0].ptr()); + value_ptrs.push_back(array[1].ptr()); + + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + + array.resize(2); + ASSERT_EQ(2u, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < array.size(); i++) { + EXPECT_FALSE(array[i].moved()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + + array.resize(3); + ASSERT_EQ(3u, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + value_ptrs.push_back(array[2].ptr()); + + size_t capacity = array.storage().capacity(); + array.resize(capacity); + ASSERT_EQ(capacity, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + for (size_t i = 3; i < array.size(); i++) + value_ptrs.push_back(array[i].ptr()); + + array.resize(capacity + 2); + ASSERT_EQ(capacity + 2, array.size()); + EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); + for (size_t i = 0; i < capacity; i++) { + EXPECT_TRUE(array[i].moved()); + EXPECT_EQ(value_ptrs[i], array[i].ptr()); + } + for (size_t i = capacity; i < array.size(); i++) + EXPECT_FALSE(array[i].moved()); + + array = nullptr; + EXPECT_EQ(0u, MoveOnlyType::num_instances()); + EXPECT_FALSE(array); + array.resize(0); + EXPECT_EQ(0u, MoveOnlyType::num_instances()); + EXPECT_TRUE(array); + } +}; + +#define ARRAY_COMMON_TEST(ArrayType, test_name) \ + TEST_F(ArrayType##Test, test_name) { \ + ArrayCommonTest<ArrayType>::test_name(); \ + } + +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/array_unittest.cc b/mojo/public/cpp/bindings/tests/array_unittest.cc index f4dad7c..0700bb1 100644 --- a/mojo/public/cpp/bindings/tests/array_unittest.cc +++ b/mojo/public/cpp/bindings/tests/array_unittest.cc @@ -4,391 +4,34 @@ #include "mojo/public/cpp/bindings/array.h" -#include <stddef.h> -#include <stdint.h> -#include <utility> - -#include "mojo/public/cpp/bindings/lib/array_internal.h" -#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" #include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/tests/array_common_test.h" #include "mojo/public/cpp/bindings/tests/container_test_util.h" -#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { namespace test { namespace { -using mojo::internal::Array_Data; -using mojo::internal::ArrayValidateParams; -using mojo::internal::FixedBufferForTesting; -using mojo::internal::String_Data; - using ArrayTest = testing::Test; -// Tests null and empty arrays. -TEST_F(ArrayTest, NullAndEmpty) { - Array<char> array0; - EXPECT_TRUE(array0.empty()); - EXPECT_FALSE(array0.is_null()); - array0 = nullptr; - EXPECT_TRUE(array0.is_null()); - EXPECT_FALSE(array0.empty()); - - Array<char> array1(nullptr); - EXPECT_TRUE(array1.is_null()); - EXPECT_FALSE(array1.empty()); - array1.SetToEmpty(); - EXPECT_TRUE(array1.empty()); - EXPECT_FALSE(array1.is_null()); -} - -// Tests that basic Array operations work. -TEST_F(ArrayTest, Basic) { - Array<char> array(8); - for (size_t i = 0; i < array.size(); ++i) { - char val = static_cast<char>(i * 2); - array[i] = val; - EXPECT_EQ(val, array.at(i)); - } -} - -// Tests that basic Array<bool> operations work. -TEST_F(ArrayTest, Bool) { - Array<bool> array(64); - for (size_t i = 0; i < array.size(); ++i) { - bool val = i % 3 == 0; - array[i] = val; - EXPECT_EQ(val, array.at(i)); - } -} - -// Tests that Array<ScopedMessagePipeHandle> supports transferring handles. -TEST_F(ArrayTest, Handle) { - MessagePipe pipe; - Array<ScopedMessagePipeHandle> handles(2); - handles[0] = std::move(pipe.handle0); - handles[1].reset(pipe.handle1.release()); - - EXPECT_FALSE(pipe.handle0.is_valid()); - EXPECT_FALSE(pipe.handle1.is_valid()); - - Array<ScopedMessagePipeHandle> handles2 = std::move(handles); - EXPECT_TRUE(handles2[0].is_valid()); - EXPECT_TRUE(handles2[1].is_valid()); - - ScopedMessagePipeHandle pipe_handle = std::move(handles2[0]); - EXPECT_TRUE(pipe_handle.is_valid()); - EXPECT_FALSE(handles2[0].is_valid()); -} - -// Tests that Array<ScopedMessagePipeHandle> supports closing handles. -TEST_F(ArrayTest, HandlesAreClosed) { - MessagePipe pipe; - MojoHandle pipe0_value = pipe.handle0.get().value(); - MojoHandle pipe1_value = pipe.handle0.get().value(); - - { - Array<ScopedMessagePipeHandle> handles(2); - handles[0] = std::move(pipe.handle0); - handles[1].reset(pipe.handle0.release()); - } - - // We expect the pipes to have been closed. - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe0_value)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe1_value)); -} - -TEST_F(ArrayTest, Clone) { - { - // Test POD. - Array<int32_t> array(3); - for (size_t i = 0; i < array.size(); ++i) - array[i] = static_cast<int32_t>(i); - - Array<int32_t> clone_array = array.Clone(); - EXPECT_EQ(array.size(), clone_array.size()); - for (size_t i = 0; i < array.size(); ++i) - EXPECT_EQ(array[i], clone_array[i]); - } - - { - // Test copyable object. - Array<String> array(2); - array[0] = "hello"; - array[1] = "world"; - - Array<String> clone_array = array.Clone(); - EXPECT_EQ(array.size(), clone_array.size()); - for (size_t i = 0; i < array.size(); ++i) - EXPECT_EQ(array[i], clone_array[i]); - } - - { - // Test struct. - Array<RectPtr> array(2); - array[1] = Rect::New(); - array[1]->x = 1; - array[1]->y = 2; - array[1]->width = 3; - array[1]->height = 4; - - Array<RectPtr> clone_array = array.Clone(); - EXPECT_EQ(array.size(), clone_array.size()); - EXPECT_TRUE(clone_array[0].is_null()); - EXPECT_EQ(array[1]->x, clone_array[1]->x); - EXPECT_EQ(array[1]->y, clone_array[1]->y); - EXPECT_EQ(array[1]->width, clone_array[1]->width); - EXPECT_EQ(array[1]->height, clone_array[1]->height); - } - - { - // Test array of array. - Array<Array<int8_t>> array(2); - array[0] = nullptr; - array[1] = Array<int8_t>(2); - array[1][0] = 0; - array[1][1] = 1; - - Array<Array<int8_t>> clone_array = array.Clone(); - EXPECT_EQ(array.size(), clone_array.size()); - EXPECT_TRUE(clone_array[0].is_null()); - EXPECT_EQ(array[1].size(), clone_array[1].size()); - EXPECT_EQ(array[1][0], clone_array[1][0]); - EXPECT_EQ(array[1][1], clone_array[1][1]); - } - - { - // Test that array of handles still works although Clone() is not available. - Array<ScopedMessagePipeHandle> array(10); - EXPECT_FALSE(array[0].is_valid()); - } -} - -TEST_F(ArrayTest, Serialization_ArrayOfPOD) { - Array<int32_t> array(4); - for (size_t i = 0; i < array.size(); ++i) - array[i] = static_cast<int32_t>(i); - - size_t size = GetSerializedSize_(array, nullptr); - EXPECT_EQ(8U + 4 * 4U, size); - - FixedBufferForTesting buf(size); - Array_Data<int32_t>* data; - ArrayValidateParams validate_params(0, false, nullptr); - SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); - - Array<int32_t> array2; - Deserialize_(data, &array2, nullptr); - - EXPECT_EQ(4U, array2.size()); - for (size_t i = 0; i < array2.size(); ++i) - EXPECT_EQ(static_cast<int32_t>(i), array2[i]); -} - -TEST_F(ArrayTest, Serialization_EmptyArrayOfPOD) { - Array<int32_t> array; - size_t size = GetSerializedSize_(array, nullptr); - EXPECT_EQ(8U, size); - - FixedBufferForTesting buf(size); - Array_Data<int32_t>* data; - ArrayValidateParams validate_params(0, false, nullptr); - SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); - - Array<int32_t> array2; - Deserialize_(data, &array2, nullptr); - EXPECT_EQ(0U, array2.size()); -} - -TEST_F(ArrayTest, Serialization_ArrayOfArrayOfPOD) { - Array<Array<int32_t>> array(2); - for (size_t j = 0; j < array.size(); ++j) { - Array<int32_t> inner(4); - for (size_t i = 0; i < inner.size(); ++i) - inner[i] = static_cast<int32_t>(i + (j * 10)); - array[j] = std::move(inner); - } - - size_t size = GetSerializedSize_(array, nullptr); - EXPECT_EQ(8U + 2 * 8U + 2 * (8U + 4 * 4U), size); - - FixedBufferForTesting buf(size); - Array_Data<Array_Data<int32_t>*>* data; - ArrayValidateParams validate_params( - 0, false, new ArrayValidateParams(0, false, nullptr)); - SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); - - Array<Array<int32_t>> array2; - Deserialize_(data, &array2, nullptr); - - EXPECT_EQ(2U, array2.size()); - for (size_t j = 0; j < array2.size(); ++j) { - const Array<int32_t>& inner = array2[j]; - EXPECT_EQ(4U, inner.size()); - for (size_t i = 0; i < inner.size(); ++i) - EXPECT_EQ(static_cast<int32_t>(i + (j * 10)), inner[i]); - } -} - -TEST_F(ArrayTest, Serialization_ArrayOfBool) { - Array<bool> array(10); - for (size_t i = 0; i < array.size(); ++i) - array[i] = i % 2 ? true : false; - - size_t size = GetSerializedSize_(array, nullptr); - EXPECT_EQ(8U + 8U, size); - - FixedBufferForTesting buf(size); - Array_Data<bool>* data; - ArrayValidateParams validate_params(0, false, nullptr); - SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); - - Array<bool> array2; - Deserialize_(data, &array2, nullptr); - - EXPECT_EQ(10U, array2.size()); - for (size_t i = 0; i < array2.size(); ++i) - EXPECT_EQ(i % 2 ? true : false, array2[i]); -} - -TEST_F(ArrayTest, Serialization_ArrayOfString) { - Array<String> array(10); - for (size_t i = 0; i < array.size(); ++i) { - char c = 'A' + static_cast<char>(i); - array[i] = String(&c, 1); - } - - size_t size = GetSerializedSize_(array, nullptr); - EXPECT_EQ(8U + // array header - 10 * 8U + // array payload (10 pointers) - 10 * (8U + // string header - 8U), // string length of 1 padded to 8 - size); - - FixedBufferForTesting buf(size); - Array_Data<String_Data*>* data; - ArrayValidateParams validate_params( - 0, false, new ArrayValidateParams(0, false, nullptr)); - SerializeArray_(std::move(array), &buf, &data, &validate_params, nullptr); - - Array<String> array2; - Deserialize_(data, &array2, nullptr); - - EXPECT_EQ(10U, array2.size()); - for (size_t i = 0; i < array2.size(); ++i) { - char c = 'A' + static_cast<char>(i); - EXPECT_EQ(String(&c, 1), array2[i]); - } -} - -TEST_F(ArrayTest, Resize_Copyable) { - ASSERT_EQ(0u, CopyableType::num_instances()); - mojo::Array<CopyableType> array(3); - std::vector<CopyableType*> value_ptrs; - value_ptrs.push_back(array[0].ptr()); - value_ptrs.push_back(array[1].ptr()); - - for (size_t i = 0; i < array.size(); i++) - array[i].ResetCopied(); - - array.resize(2); - ASSERT_EQ(2u, array.size()); - EXPECT_EQ(array.size(), CopyableType::num_instances()); - for (size_t i = 0; i < array.size(); i++) { - EXPECT_FALSE(array[i].copied()); - EXPECT_EQ(value_ptrs[i], array[i].ptr()); - } - - array.resize(3); - array[2].ResetCopied(); - ASSERT_EQ(3u, array.size()); - EXPECT_EQ(array.size(), CopyableType::num_instances()); - for (size_t i = 0; i < array.size(); i++) - EXPECT_FALSE(array[i].copied()); - value_ptrs.push_back(array[2].ptr()); - - size_t capacity = array.storage().capacity(); - array.resize(capacity); - ASSERT_EQ(capacity, array.size()); - EXPECT_EQ(array.size(), CopyableType::num_instances()); - for (size_t i = 0; i < 3; i++) - EXPECT_FALSE(array[i].copied()); - for (size_t i = 3; i < array.size(); i++) { - array[i].ResetCopied(); - value_ptrs.push_back(array[i].ptr()); - } - - array.resize(capacity + 2); - ASSERT_EQ(capacity + 2, array.size()); - EXPECT_EQ(array.size(), CopyableType::num_instances()); - for (size_t i = 0; i < capacity; i++) { - EXPECT_TRUE(array[i].copied()); - EXPECT_EQ(value_ptrs[i], array[i].ptr()); - } - array = nullptr; - EXPECT_EQ(0u, CopyableType::num_instances()); - EXPECT_FALSE(array); - array.resize(0); - EXPECT_EQ(0u, CopyableType::num_instances()); - EXPECT_TRUE(array); -} - -TEST_F(ArrayTest, Resize_MoveOnly) { - ASSERT_EQ(0u, MoveOnlyType::num_instances()); - mojo::Array<MoveOnlyType> array(3); - std::vector<MoveOnlyType*> value_ptrs; - value_ptrs.push_back(array[0].ptr()); - value_ptrs.push_back(array[1].ptr()); - - for (size_t i = 0; i < array.size(); i++) - EXPECT_FALSE(array[i].moved()); - - array.resize(2); - ASSERT_EQ(2u, array.size()); - EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); - for (size_t i = 0; i < array.size(); i++) { - EXPECT_FALSE(array[i].moved()); - EXPECT_EQ(value_ptrs[i], array[i].ptr()); - } - - array.resize(3); - ASSERT_EQ(3u, array.size()); - EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); - for (size_t i = 0; i < array.size(); i++) - EXPECT_FALSE(array[i].moved()); - value_ptrs.push_back(array[2].ptr()); - - size_t capacity = array.storage().capacity(); - array.resize(capacity); - ASSERT_EQ(capacity, array.size()); - EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); - for (size_t i = 0; i < array.size(); i++) - EXPECT_FALSE(array[i].moved()); - for (size_t i = 3; i < array.size(); i++) - value_ptrs.push_back(array[i].ptr()); - - array.resize(capacity + 2); - ASSERT_EQ(capacity + 2, array.size()); - EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); - for (size_t i = 0; i < capacity; i++) { - EXPECT_TRUE(array[i].moved()); - EXPECT_EQ(value_ptrs[i], array[i].ptr()); - } - for (size_t i = capacity; i < array.size(); i++) - EXPECT_FALSE(array[i].moved()); - - array = nullptr; - EXPECT_EQ(0u, MoveOnlyType::num_instances()); - EXPECT_FALSE(array); - array.resize(0); - EXPECT_EQ(0u, MoveOnlyType::num_instances()); - EXPECT_TRUE(array); -} +ARRAY_COMMON_TEST(Array, NullAndEmpty) +ARRAY_COMMON_TEST(Array, Basic) +ARRAY_COMMON_TEST(Array, Bool) +ARRAY_COMMON_TEST(Array, Handle) +ARRAY_COMMON_TEST(Array, HandlesAreClosed) +ARRAY_COMMON_TEST(Array, Clone) +ARRAY_COMMON_TEST(Array, Serialization_ArrayOfPOD) +ARRAY_COMMON_TEST(Array, Serialization_EmptyArrayOfPOD) +ARRAY_COMMON_TEST(Array, Serialization_ArrayOfArrayOfPOD) +ARRAY_COMMON_TEST(Array, Serialization_ArrayOfBool) +ARRAY_COMMON_TEST(Array, Serialization_ArrayOfString) +ARRAY_COMMON_TEST(Array, Resize_Copyable) +ARRAY_COMMON_TEST(Array, Resize_MoveOnly) TEST_F(ArrayTest, PushBack_Copyable) { ASSERT_EQ(0u, CopyableType::num_instances()); - mojo::Array<CopyableType> array(2); + Array<CopyableType> array(2); array = nullptr; std::vector<CopyableType*> value_ptrs; size_t capacity = array.storage().capacity(); @@ -423,7 +66,7 @@ TEST_F(ArrayTest, PushBack_Copyable) { TEST_F(ArrayTest, PushBack_MoveOnly) { ASSERT_EQ(0u, MoveOnlyType::num_instances()); - mojo::Array<MoveOnlyType> array(2); + Array<MoveOnlyType> array(2); array = nullptr; std::vector<MoveOnlyType*> value_ptrs; size_t capacity = array.storage().capacity(); @@ -458,7 +101,7 @@ TEST_F(ArrayTest, PushBack_MoveOnly) { TEST_F(ArrayTest, MoveFromAndToSTLVector_Copyable) { std::vector<CopyableType> vec1(1); - mojo::Array<CopyableType> arr(std::move(vec1)); + Array<CopyableType> arr(std::move(vec1)); ASSERT_EQ(1u, arr.size()); ASSERT_FALSE(arr[0].copied()); @@ -472,7 +115,7 @@ TEST_F(ArrayTest, MoveFromAndToSTLVector_Copyable) { TEST_F(ArrayTest, MoveFromAndToSTLVector_MoveOnly) { std::vector<MoveOnlyType> vec1(1); - mojo::Array<MoveOnlyType> arr(std::move(vec1)); + Array<MoveOnlyType> arr(std::move(vec1)); ASSERT_EQ(1u, arr.size()); diff --git a/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc new file mode 100644 index 0000000..bb54b9c --- /dev/null +++ b/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc @@ -0,0 +1,62 @@ +// 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_array.h" + +#include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/lib/wtf_serialization.h" +#include "mojo/public/cpp/bindings/tests/array_common_test.h" +#include "mojo/public/cpp/bindings/tests/container_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +using WTFArrayTest = testing::Test; + +ARRAY_COMMON_TEST(WTFArray, NullAndEmpty) +ARRAY_COMMON_TEST(WTFArray, Basic) +ARRAY_COMMON_TEST(WTFArray, Bool) +ARRAY_COMMON_TEST(WTFArray, Handle) +ARRAY_COMMON_TEST(WTFArray, HandlesAreClosed) +ARRAY_COMMON_TEST(WTFArray, Clone) +ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfPOD) +ARRAY_COMMON_TEST(WTFArray, Serialization_EmptyArrayOfPOD) +ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfArrayOfPOD) +ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfBool) +ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfString) +ARRAY_COMMON_TEST(WTFArray, Resize_Copyable) +ARRAY_COMMON_TEST(WTFArray, Resize_MoveOnly) + +TEST_F(WTFArrayTest, MoveFromAndToWTFVector_Copyable) { + WTF::Vector<CopyableType> vec1(1); + WTFArray<CopyableType> arr(std::move(vec1)); + ASSERT_EQ(1u, arr.size()); + ASSERT_FALSE(arr[0].copied()); + + WTF::Vector<CopyableType> vec2(arr.PassStorage()); + ASSERT_EQ(1u, vec2.size()); + ASSERT_FALSE(vec2[0].copied()); + + ASSERT_EQ(0u, arr.size()); + ASSERT_TRUE(arr.is_null()); +} + +TEST_F(WTFArrayTest, MoveFromAndToWTFVector_MoveOnly) { + WTF::Vector<MoveOnlyType> vec1(1); + WTFArray<MoveOnlyType> arr(std::move(vec1)); + + ASSERT_EQ(1u, arr.size()); + + WTF::Vector<MoveOnlyType> vec2(arr.PassStorage()); + ASSERT_EQ(1u, vec2.size()); + + ASSERT_EQ(0u, arr.size()); + ASSERT_TRUE(arr.is_null()); +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc index 20486eb..ef781bc 100644 --- a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc +++ b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc @@ -34,6 +34,11 @@ class TestWTFImpl : public TestWTF { callback.Run(str); } + void EchoStringArray(Array<String> arr, + const EchoStringArrayCallback& callback) override { + callback.Run(std::move(arr)); + } + private: Binding<TestWTF> binding_; }; @@ -46,17 +51,22 @@ class WTFTypesTest : public testing::Test { base::MessageLoop loop_; }; -} // namespace - -TEST_F(WTFTypesTest, WTFToWTFStringSerialization) { - Array<WTF::String> strs(4); +WTFArray<WTF::String> ConstructStringArray() { + WTFArray<WTF::String> strs(4); // strs[0] is null. // strs[1] is empty. strs[1] = ""; strs[2] = kHelloWorld; strs[3] = WTF::String::fromUTF8(kUTF8HelloWorld); - Array<WTF::String> cloned_strs = strs.Clone(); + return strs; +} + +} // namespace + +TEST_F(WTFTypesTest, WTFToWTFSerialization_StringArray) { + WTFArray<WTF::String> strs = ConstructStringArray(); + WTFArray<WTF::String> cloned_strs = strs.Clone(); mojo::internal::SerializationContext context; size_t size = GetSerializedSize_(cloned_strs, &context); @@ -68,31 +78,23 @@ TEST_F(WTFTypesTest, WTFToWTFStringSerialization) { SerializeArray_(std::move(cloned_strs), &buf, &data, &validate_params, &context); - Array<WTF::String> strs2; + WTFArray<WTF::String> strs2; Deserialize_(data, &strs2, nullptr); EXPECT_TRUE(strs.Equals(strs2)); } -TEST_F(WTFTypesTest, WTFToMojoStringSerialization) { - Array<WTF::String> strs(4); - // strs[0] is null. - // strs[1] is empty. - strs[1] = ""; - strs[2] = kHelloWorld; - strs[3] = WTF::String::fromUTF8(kUTF8HelloWorld); - - Array<WTF::String> cloned_strs = strs.Clone(); +TEST_F(WTFTypesTest, WTFToMojoSerialization_StringArray) { + WTFArray<WTF::String> strs = ConstructStringArray(); mojo::internal::SerializationContext context; - size_t size = GetSerializedSize_(cloned_strs, &context); + size_t size = GetSerializedSize_(strs, &context); mojo::internal::FixedBufferForTesting buf(size); mojo::internal::Array_Data<mojo::internal::String_Data*>* data; mojo::internal::ArrayValidateParams validate_params( 0, true, new mojo::internal::ArrayValidateParams(0, false, nullptr)); - SerializeArray_(std::move(cloned_strs), &buf, &data, &validate_params, - &context); + SerializeArray_(std::move(strs), &buf, &data, &validate_params, &context); Array<mojo::String> strs2; Deserialize_(data, &strs2, nullptr); @@ -108,14 +110,9 @@ TEST_F(WTFTypesTest, SendString) { blink::TestWTFPtr ptr; TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr))); - WTF::String strs[4]; - // strs[0] is null. - // strs[1] is empty. - strs[1] = ""; - strs[2] = kHelloWorld; - strs[3] = WTF::String::fromUTF8(kUTF8HelloWorld); + WTFArray<WTF::String> strs = ConstructStringArray(); - for (size_t i = 0; i < arraysize(strs); ++i) { + for (size_t i = 0; i < strs.size(); ++i) { base::RunLoop loop; // Test that a WTF::String is unchanged after the following conversion: // - serialized; @@ -130,5 +127,33 @@ TEST_F(WTFTypesTest, SendString) { } } +TEST_F(WTFTypesTest, SendStringArray) { + blink::TestWTFPtr ptr; + TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr))); + + WTFArray<WTF::String> arrs[3]; + // arrs[0] is empty. + // arrs[1] is null. + arrs[1] = nullptr; + arrs[2] = ConstructStringArray(); + + for (size_t i = 0; i < arraysize(arrs); ++i) { + WTFArray<WTF::String> expected_arr = arrs[i].Clone(); + base::RunLoop loop; + // Test that a mojo::WTFArray<WTF::String> is unchanged after the following + // conversion: + // - serialized; + // - deserialized as mojo::Array<mojo::String>; + // - serialized; + // - deserialized as mojo::WTFArray<WTF::String>. + ptr->EchoStringArray(std::move(arrs[i]), + [&loop, &expected_arr, &i](WTFArray<WTF::String> arr) { + EXPECT_TRUE(expected_arr.Equals(arr)); + loop.Quit(); + }); + loop.Run(); + } +} + } // namespace test } // namespace mojo diff --git a/mojo/public/cpp/bindings/wtf_array.h b/mojo/public/cpp/bindings/wtf_array.h new file mode 100644 index 0000000..b5d1a30 --- /dev/null +++ b/mojo/public/cpp/bindings/wtf_array.h @@ -0,0 +1,235 @@ +// 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_ARRAY_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_ + +#include <stddef.h> +#include <utility> + +#include "base/move.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/lib/value_traits.h" +#include "mojo/public/cpp/bindings/type_converter.h" +#include "third_party/WebKit/Source/wtf/Vector.h" + +namespace mojo { + +// Represents an array backed by WTF::Vector. Comparing with WTF::Vector, +// mojo::WTFArray is move-only and can be null. +// It is easy to convert between WTF::Vector<T> and mojo::WTFArray<T>: +// - constructor WTFArray(WTF::Vector<T>&&) takes the contents of a +// WTF::Vector<T>; +// - method PassStorage() passes the underlying WTF::Vector. +template <typename T> +class WTFArray { + MOVE_ONLY_TYPE_FOR_CPP_03(WTFArray); + + public: + using Data_ = + internal::Array_Data<typename internal::WrapperTraits<T>::DataType>; + using ElementType = T; + + // Constructs an empty array. + WTFArray() : is_null_(false) {} + // Constructs a null array. + WTFArray(std::nullptr_t null_pointer) : is_null_(true) {} + + // Constructs a new non-null array of the specified size. The elements will + // be value-initialized (meaning that they will be initialized by their + // default constructor, if any, or else zero-initialized). + explicit WTFArray(size_t size) : vec_(size), is_null_(false) {} + ~WTFArray() {} + + // Moves the contents of |other| into this array. + WTFArray(WTF::Vector<T>&& other) : vec_(std::move(other)), is_null_(false) {} + WTFArray(WTFArray&& other) : is_null_(true) { Take(&other); } + + WTFArray& operator=(WTF::Vector<T>&& other) { + vec_ = std::move(other); + is_null_ = false; + return *this; + } + WTFArray& operator=(WTFArray&& other) { + Take(&other); + return *this; + } + + WTFArray& operator=(std::nullptr_t null_pointer) { + is_null_ = true; + vec_.clear(); + return *this; + } + + // Creates a non-null array of the specified size. The elements will be + // value-initialized (meaning that they will be initialized by their default + // constructor, if any, or else zero-initialized). + static WTFArray New(size_t size) { return WTFArray(size); } + + // Creates a new array with a copy of the contents of |other|. + template <typename U> + static WTFArray From(const U& other) { + return TypeConverter<WTFArray, U>::Convert(other); + } + + // Copies the contents of this array to a new object of type |U|. + template <typename U> + U To() const { + return TypeConverter<U, WTFArray>::Convert(*this); + } + + // Indicates whether the array is null (which is distinct from empty). + bool is_null() const { + // When the array is set to null, the underlying storage |vec_| shouldn't + // contain any elements. + DCHECK(!is_null_ || vec_.isEmpty()); + return is_null_; + } + + // Indicates whether the array is empty (which is distinct from null). + bool empty() const { return vec_.isEmpty() && !is_null_; } + + // Returns a reference to the first element of the array. Calling this on a + // null or empty array causes undefined behavior. + const T& front() const { return vec_.first(); } + T& front() { return vec_.first(); } + + // Returns the size of the array, which will be zero if the array is null. + size_t size() const { return vec_.size(); } + + // Returns a reference to the element at zero-based |offset|. Calling this on + // an array with size less than |offset|+1 causes undefined behavior. + const T& at(size_t offset) const { return vec_.at(offset); } + const T& operator[](size_t offset) const { return at(offset); } + T& at(size_t offset) { return vec_.at(offset); } + T& operator[](size_t offset) { return at(offset); } + + // Resizes the array to |size| and makes it non-null. Otherwise, works just + // like the resize method of |WTF::Vector|. + void resize(size_t size) { + is_null_ = false; + vec_.resize(size); + } + + // Sets the array to empty (even if previously it was null.) + void SetToEmpty() { resize(0); } + + // Returns a const reference to the |WTF::Vector| managed by this class. If + // the array is null, this will be an empty vector. + const WTF::Vector<T>& storage() const { return vec_; } + + // Passes the underlying storage and resets this array to null. + // + // TODO(yzshen): Consider changing this to a rvalue-ref-qualified conversion + // to WTF::Vector<T> after we move to MSVC 2015. + WTF::Vector<T> PassStorage() { + is_null_ = true; + return std::move(vec_); + } + + void Swap(WTFArray* other) { + std::swap(is_null_, other->is_null_); + vec_.swap(other->vec_); + } + + // Swaps the contents of this array with the specified vector, making this + // array non-null. Since the vector cannot represent null, it will just be + // made empty if this array is null. + void Swap(WTF::Vector<T>* other) { + is_null_ = false; + vec_.swap(*other); + } + + // Returns a copy of the array where each value of the new array has been + // "cloned" from the corresponding value of this array. If this array contains + // primitive data types, this is equivalent to simply copying the contents. + // However, if the array contains objects, then each new element is created by + // calling the |Clone| method of the source element, which should make a copy + // of the element. + // + // Please note that calling this method will fail compilation if the element + // type cannot be cloned (which usually means that it is a Mojo handle type or + // a type contains Mojo handles). + WTFArray Clone() const { + WTFArray result; + result.is_null_ = is_null_; + CloneTraits<T>::Clone(vec_, &result.vec_); + return std::move(result); + } + + // Indicates whether the contents of this array are equal to |other|. A null + // array is only equal to another null array. Elements are compared using the + // |ValueTraits::Equals| method, which in most cases calls the |Equals| method + // of the element. + bool Equals(const WTFArray& other) const { + if (is_null() != other.is_null()) + return false; + if (size() != other.size()) + return false; + for (size_t i = 0; i < size(); ++i) { + if (!internal::ValueTraits<T>::Equals(at(i), other.at(i))) + return false; + } + return true; + } + + private: + typedef WTF::Vector<T> WTFArray::*Testable; + + public: + operator Testable() const { + // When the array is set to null, the underlying storage |vec_| shouldn't + // contain any elements. + DCHECK(!is_null_ || vec_.isEmpty()); + return is_null_ ? 0 : &WTFArray::vec_; + } + + private: + // Forbid the == and != operators explicitly, otherwise WTFArray will be + // converted to Testable to do == or != comparison. + template <typename U> + bool operator==(const WTFArray<U>& other) const = delete; + template <typename U> + bool operator!=(const WTFArray<U>& other) const = delete; + + template <typename U, + bool is_move_only_type = internal::IsMoveOnlyType<U>::value> + struct CloneTraits {}; + + template <typename U> + struct CloneTraits<U, false> { + static inline void Clone(const WTF::Vector<T>& src_vec, + WTF::Vector<T>* dest_vec) { + DCHECK(dest_vec->isEmpty()); + dest_vec->reserveCapacity(src_vec.size()); + for (const auto& element : src_vec) + dest_vec->append(element); + } + }; + + template <typename U> + struct CloneTraits<U, true> { + static inline void Clone(const WTF::Vector<T>& src_vec, + WTF::Vector<T>* dest_vec) { + DCHECK(dest_vec->isEmpty()); + dest_vec->reserveCapacity(src_vec.size()); + for (const auto& element : src_vec) + dest_vec->append(element.Clone()); + } + }; + + void Take(WTFArray* other) { + operator=(nullptr); + Swap(other); + } + + WTF::Vector<T> vec_; + bool is_null_; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_ diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn index c9b01b4..18ea38b 100644 --- a/mojo/public/interfaces/bindings/tests/BUILD.gn +++ b/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -113,10 +113,6 @@ mojom("test_wtf_types_blink") { "test_wtf_types.mojom", ] - deps = [ - ":test_wtf_types", - ] - for_blink = true variant = "blink" } diff --git a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom index f776309..9e79a18 100644 --- a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom +++ b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom @@ -7,8 +7,14 @@ module mojo.test; struct TestWTFCodeGeneration { string str; string? nullable_str; + array<string> strs; + array<string?> nullable_strs; + array<array<int32>> arrays; + array<bool> bools; + array<handle<message_pipe>> handles; }; interface TestWTF { EchoString(string? str) => (string? str); + EchoStringArray(array<string?>? arr) => (array<string?>? arr); }; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl index 8046116e..901fa69 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -19,7 +19,6 @@ #include <ostream> #include "base/strings/string_piece.h" -#include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/associated_interface_request.h" @@ -39,8 +38,10 @@ #include "{{import.module.path}}.h" {%- endfor %} {%- if not for_blink %} +#include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/string.h" {%- else %} +#include "mojo/public/cpp/bindings/wtf_array.h" #include "third_party/WebKit/Source/wtf/text/WTFString.h" {%- endif %} diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index 4433740..40b5acc 100644 --- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -175,7 +175,8 @@ def GetCppArrayArgWrapperType(kind): if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return "%sPtr" % GetNameForKind(kind) if mojom.IsArrayKind(kind): - return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.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)) @@ -212,7 +213,8 @@ def GetCppResultWrapperType(kind): if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return "%sPtr" % GetNameForKind(kind) if mojom.IsArrayKind(kind): - return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.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)) @@ -254,7 +256,8 @@ def GetCppWrapperType(kind): if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return "%sPtr" % GetNameForKind(kind) if mojom.IsArrayKind(kind): - return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.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)) @@ -288,7 +291,8 @@ def GetCppConstWrapperType(kind): if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return "%sPtr" % GetNameForKind(kind) if mojom.IsArrayKind(kind): - return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.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)) |