summaryrefslogtreecommitdiffstats
path: root/mojo/public/cpp
diff options
context:
space:
mode:
authorrockot <rockot@chromium.org>2015-11-12 17:33:59 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-13 01:34:47 +0000
commit85dce086001825a2faa4e75755a669f5e08a1cad (patch)
tree722de1d974f799b3d1ee1ca4c81bb8b0fa75a95d /mojo/public/cpp
parent415b73b1a400a994a86e6f29709aa0271e895dd5 (diff)
downloadchromium_src-85dce086001825a2faa4e75755a669f5e08a1cad.zip
chromium_src-85dce086001825a2faa4e75755a669f5e08a1cad.tar.gz
chromium_src-85dce086001825a2faa4e75755a669f5e08a1cad.tar.bz2
Move third_party/mojo/src/mojo/public to mojo/public
BUG=None NOPRESUBMIT=true Review URL: https://codereview.chromium.org/1410053006 Cr-Commit-Position: refs/heads/master@{#359461}
Diffstat (limited to 'mojo/public/cpp')
-rw-r--r--mojo/public/cpp/README.md71
-rw-r--r--mojo/public/cpp/bindings/BUILD.gn95
-rw-r--r--mojo/public/cpp/bindings/array.h249
-rw-r--r--mojo/public/cpp/bindings/associated_interface_ptr_info.h32
-rw-r--r--mojo/public/cpp/bindings/associated_interface_request.h31
-rw-r--r--mojo/public/cpp/bindings/binding.h239
-rw-r--r--mojo/public/cpp/bindings/callback.h115
-rw-r--r--mojo/public/cpp/bindings/interface_ptr.h197
-rw-r--r--mojo/public/cpp/bindings/interface_ptr_info.h58
-rw-r--r--mojo/public/cpp/bindings/interface_request.h118
-rw-r--r--mojo/public/cpp/bindings/lib/TODO4
-rw-r--r--mojo/public/cpp/bindings/lib/array_internal.cc74
-rw-r--r--mojo/public/cpp/bindings/lib/array_internal.h534
-rw-r--r--mojo/public/cpp/bindings/lib/array_serialization.h338
-rw-r--r--mojo/public/cpp/bindings/lib/bindings_internal.h134
-rw-r--r--mojo/public/cpp/bindings/lib/bindings_serialization.cc90
-rw-r--r--mojo/public/cpp/bindings/lib/bindings_serialization.h91
-rw-r--r--mojo/public/cpp/bindings/lib/bounds_checker.cc77
-rw-r--r--mojo/public/cpp/bindings/lib/bounds_checker.h63
-rw-r--r--mojo/public/cpp/bindings/lib/buffer.h24
-rw-r--r--mojo/public/cpp/bindings/lib/callback_internal.h53
-rw-r--r--mojo/public/cpp/bindings/lib/connector.cc259
-rw-r--r--mojo/public/cpp/bindings/lib/connector.h142
-rw-r--r--mojo/public/cpp/bindings/lib/control_message_handler.cc81
-rw-r--r--mojo/public/cpp/bindings/lib/control_message_handler.h42
-rw-r--r--mojo/public/cpp/bindings/lib/control_message_proxy.cc100
-rw-r--r--mojo/public/cpp/bindings/lib/control_message_proxy.h38
-rw-r--r--mojo/public/cpp/bindings/lib/filter_chain.cc49
-rw-r--r--mojo/public/cpp/bindings/lib/filter_chain.h64
-rw-r--r--mojo/public/cpp/bindings/lib/fixed_buffer.cc60
-rw-r--r--mojo/public/cpp/bindings/lib/fixed_buffer.h78
-rw-r--r--mojo/public/cpp/bindings/lib/interface_ptr_internal.h174
-rw-r--r--mojo/public/cpp/bindings/lib/map_data_internal.h141
-rw-r--r--mojo/public/cpp/bindings/lib/map_internal.h220
-rw-r--r--mojo/public/cpp/bindings/lib/map_serialization.h182
-rw-r--r--mojo/public/cpp/bindings/lib/message.cc105
-rw-r--r--mojo/public/cpp/bindings/lib/message_builder.cc52
-rw-r--r--mojo/public/cpp/bindings/lib/message_builder.h68
-rw-r--r--mojo/public/cpp/bindings/lib/message_filter.cc23
-rw-r--r--mojo/public/cpp/bindings/lib/message_header_validator.cc77
-rw-r--r--mojo/public/cpp/bindings/lib/message_header_validator.h24
-rw-r--r--mojo/public/cpp/bindings/lib/message_internal.h50
-rw-r--r--mojo/public/cpp/bindings/lib/no_interface.cc20
-rw-r--r--mojo/public/cpp/bindings/lib/router.cc158
-rw-r--r--mojo/public/cpp/bindings/lib/router.h141
-rw-r--r--mojo/public/cpp/bindings/lib/shared_data.h83
-rw-r--r--mojo/public/cpp/bindings/lib/shared_ptr.h57
-rw-r--r--mojo/public/cpp/bindings/lib/string_serialization.cc40
-rw-r--r--mojo/public/cpp/bindings/lib/string_serialization.h21
-rw-r--r--mojo/public/cpp/bindings/lib/template_util.h130
-rw-r--r--mojo/public/cpp/bindings/lib/thread_checker.h37
-rw-r--r--mojo/public/cpp/bindings/lib/thread_checker_posix.cc24
-rw-r--r--mojo/public/cpp/bindings/lib/thread_checker_posix.h31
-rw-r--r--mojo/public/cpp/bindings/lib/union_accessor.h33
-rw-r--r--mojo/public/cpp/bindings/lib/validate_params.h49
-rw-r--r--mojo/public/cpp/bindings/lib/validation_errors.cc98
-rw-r--r--mojo/public/cpp/bindings/lib/validation_errors.h122
-rw-r--r--mojo/public/cpp/bindings/lib/validation_util.cc103
-rw-r--r--mojo/public/cpp/bindings/lib/validation_util.h53
-rw-r--r--mojo/public/cpp/bindings/lib/value_traits.h108
-rw-r--r--mojo/public/cpp/bindings/map.h296
-rw-r--r--mojo/public/cpp/bindings/message.h159
-rw-r--r--mojo/public/cpp/bindings/message_filter.h39
-rw-r--r--mojo/public/cpp/bindings/no_interface.h52
-rw-r--r--mojo/public/cpp/bindings/string.h175
-rw-r--r--mojo/public/cpp/bindings/strong_binding.h127
-rw-r--r--mojo/public/cpp/bindings/struct_ptr.h206
-rw-r--r--mojo/public/cpp/bindings/tests/BUILD.gn86
-rw-r--r--mojo/public/cpp/bindings/tests/array_unittest.cc438
-rw-r--r--mojo/public/cpp/bindings/tests/binding_callback_unittest.cc305
-rw-r--r--mojo/public/cpp/bindings/tests/binding_unittest.cc364
-rw-r--r--mojo/public/cpp/bindings/tests/bindings_perftest.cc127
-rw-r--r--mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc209
-rw-r--r--mojo/public/cpp/bindings/tests/buffer_unittest.cc91
-rw-r--r--mojo/public/cpp/bindings/tests/callback_unittest.cc196
-rw-r--r--mojo/public/cpp/bindings/tests/connector_unittest.cc443
-rw-r--r--mojo/public/cpp/bindings/tests/constant_unittest.cc42
-rw-r--r--mojo/public/cpp/bindings/tests/container_test_util.cc50
-rw-r--r--mojo/public/cpp/bindings/tests/container_test_util.h52
-rw-r--r--mojo/public/cpp/bindings/tests/equals_unittest.cc158
-rw-r--r--mojo/public/cpp/bindings/tests/handle_passing_unittest.cc353
-rw-r--r--mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc647
-rw-r--r--mojo/public/cpp/bindings/tests/map_unittest.cc316
-rw-r--r--mojo/public/cpp/bindings/tests/message_queue.cc43
-rw-r--r--mojo/public/cpp/bindings/tests/message_queue.h43
-rw-r--r--mojo/public/cpp/bindings/tests/request_response_unittest.cc152
-rw-r--r--mojo/public/cpp/bindings/tests/router_unittest.cc379
-rw-r--r--mojo/public/cpp/bindings/tests/sample_service_unittest.cc371
-rw-r--r--mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc227
-rw-r--r--mojo/public/cpp/bindings/tests/string_unittest.cc93
-rw-r--r--mojo/public/cpp/bindings/tests/struct_unittest.cc415
-rw-r--r--mojo/public/cpp/bindings/tests/type_conversion_unittest.cc204
-rw-r--r--mojo/public/cpp/bindings/tests/union_unittest.cc1131
-rw-r--r--mojo/public/cpp/bindings/tests/validation_test_input_parser.cc410
-rw-r--r--mojo/public/cpp/bindings/tests/validation_test_input_parser.h120
-rw-r--r--mojo/public/cpp/bindings/tests/validation_unittest.cc480
-rw-r--r--mojo/public/cpp/bindings/tests/versioning_apptest.cc121
-rw-r--r--mojo/public/cpp/bindings/tests/versioning_test_service.cc125
-rw-r--r--mojo/public/cpp/bindings/type_converter.h92
-rw-r--r--mojo/public/cpp/environment/BUILD.gn47
-rw-r--r--mojo/public/cpp/environment/async_waiter.h40
-rw-r--r--mojo/public/cpp/environment/environment.h53
-rw-r--r--mojo/public/cpp/environment/lib/async_waiter.cc34
-rw-r--r--mojo/public/cpp/environment/lib/default_async_waiter.cc85
-rw-r--r--mojo/public/cpp/environment/lib/default_async_waiter.h18
-rw-r--r--mojo/public/cpp/environment/lib/default_logger.cc77
-rw-r--r--mojo/public/cpp/environment/lib/default_logger.h18
-rw-r--r--mojo/public/cpp/environment/lib/default_task_tracker.cc40
-rw-r--r--mojo/public/cpp/environment/lib/default_task_tracker.h19
-rw-r--r--mojo/public/cpp/environment/lib/environment.cc93
-rw-r--r--mojo/public/cpp/environment/lib/logging.cc45
-rw-r--r--mojo/public/cpp/environment/lib/scoped_task_tracking.cc37
-rw-r--r--mojo/public/cpp/environment/lib/scoped_task_tracking.h34
-rw-r--r--mojo/public/cpp/environment/logging.h90
-rw-r--r--mojo/public/cpp/environment/task_tracker.h30
-rw-r--r--mojo/public/cpp/environment/tests/BUILD.gn29
-rw-r--r--mojo/public/cpp/environment/tests/async_wait_unittest.cc114
-rw-r--r--mojo/public/cpp/environment/tests/async_waiter_unittest.cc107
-rw-r--r--mojo/public/cpp/environment/tests/logger_unittest.cc89
-rw-r--r--mojo/public/cpp/environment/tests/logging_unittest.cc521
-rw-r--r--mojo/public/cpp/system/BUILD.gn19
-rw-r--r--mojo/public/cpp/system/buffer.h132
-rw-r--r--mojo/public/cpp/system/core.h15
-rw-r--r--mojo/public/cpp/system/data_pipe.h162
-rw-r--r--mojo/public/cpp/system/functions.h32
-rw-r--r--mojo/public/cpp/system/handle.h290
-rw-r--r--mojo/public/cpp/system/macros.h81
-rw-r--r--mojo/public/cpp/system/message_pipe.h119
-rw-r--r--mojo/public/cpp/system/tests/BUILD.gn25
-rw-r--r--mojo/public/cpp/system/tests/core_unittest.cc495
-rw-r--r--mojo/public/cpp/system/tests/macros_unittest.cc159
-rw-r--r--mojo/public/cpp/test_support/BUILD.gn25
-rw-r--r--mojo/public/cpp/test_support/lib/test_support.cc26
-rw-r--r--mojo/public/cpp/test_support/lib/test_utils.cc97
-rw-r--r--mojo/public/cpp/test_support/test_support.h35
-rw-r--r--mojo/public/cpp/test_support/test_utils.h40
-rw-r--r--mojo/public/cpp/utility/BUILD.gn35
-rw-r--r--mojo/public/cpp/utility/lib/mutex.cc52
-rw-r--r--mojo/public/cpp/utility/lib/run_loop.cc267
-rw-r--r--mojo/public/cpp/utility/lib/thread.cc64
-rw-r--r--mojo/public/cpp/utility/lib/thread_local.h54
-rw-r--r--mojo/public/cpp/utility/lib/thread_local_posix.cc39
-rw-r--r--mojo/public/cpp/utility/lib/thread_local_win.cc39
-rw-r--r--mojo/public/cpp/utility/mutex.h70
-rw-r--r--mojo/public/cpp/utility/run_loop.h156
-rw-r--r--mojo/public/cpp/utility/run_loop_handler.h25
-rw-r--r--mojo/public/cpp/utility/tests/BUILD.gn32
-rw-r--r--mojo/public/cpp/utility/tests/mutex_unittest.cc259
-rw-r--r--mojo/public/cpp/utility/tests/run_loop_unittest.cc425
-rw-r--r--mojo/public/cpp/utility/tests/thread_unittest.cc106
-rw-r--r--mojo/public/cpp/utility/thread.h62
151 files changed, 20418 insertions, 0 deletions
diff --git a/mojo/public/cpp/README.md b/mojo/public/cpp/README.md
new file mode 100644
index 0000000..4404c24
--- /dev/null
+++ b/mojo/public/cpp/README.md
@@ -0,0 +1,71 @@
+Mojo Public C++ API
+===================
+
+This directory contains C++ language bindings for the Mojo Public API.
+
+A number of subdirectories provide wrappers for the lower-level C APIs (in
+subdirectories of the same name, under mojo/public/c/). Typically, these
+wrappers provide increased convenience and/or type-safety.
+
+Other subdirectories provide support (static) libraries of various sorts. In
+this case, the organization is to have the public interface for the library
+defined in header files in the subdirectory itself and the implementation of the
+library at a lower level, under a lib (sub)subdirectory. A developer should be
+able to substitute their own implementation of any such support library, and
+expect other support libraries, which may depend on that library, to work
+properly.
+
+Bindings
+--------
+
+The bindings/ subdirectory contains a support (static) library needed by the
+code generated by the bindings generator tool (in mojo/public/tools/bindings/),
+which translates Mojo IDL (.mojom) files into idiomatic C++ (among other
+languages).
+
+This library depends on the Environment library.
+
+Environment
+-----------
+
+The environment/ subdirectory contains a support (static) library that
+represents shared state needed to support the Bindings and GLES2 libraries.
+
+This library depends on the Utility library.
+
+
+GLES2
+-----
+
+The gles2/ subdirectory contains C++ wrappers (and some additional helpers) of
+the API defined in mojo/public/c/gles2/ (which provides access to GLES2).
+
+These wrappers depend on the Environment library.
+
+Shell
+-----
+
+The shell/ subdirectory contains a support (static) library that aids in writing
+Mojo applications and interacting with the Shell service.
+
+System
+------
+
+The system/ subdirectory contains C++ wrappers (and some additional helpers) of
+the API defined in mojo/public/c/system/, which defines the basic, "core" API,
+especially used to communicate with Mojo services.
+
+Test Support
+------------
+
+The test_support/ subdirectory contains C++ wrappers of the test-only API
+defined in mojo/public/c/test_support/. It is not meant for general use by Mojo
+applications.
+
+Utility
+-------
+
+The utility/ subdirectory contains a support (static) library that provides
+various basic functionality. Most notably, it provides an implementation of a
+RunLoop based on MojoWaitMany() that applications may use as the basis for
+asynchronous message processing.
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
new file mode 100644
index 0000000..89679f2
--- /dev/null
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -0,0 +1,95 @@
+# Copyright 2014 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.
+
+import("../../mojo_sdk.gni")
+
+mojo_sdk_source_set("bindings") {
+ sources = [
+ "array.h",
+ "associated_interface_ptr_info.h",
+ "associated_interface_request.h",
+ "binding.h",
+ "interface_ptr.h",
+ "interface_ptr_info.h",
+ "interface_request.h",
+ "lib/array_internal.cc",
+ "lib/array_internal.h",
+ "lib/array_serialization.h",
+ "lib/bindings_internal.h",
+ "lib/bindings_serialization.cc",
+ "lib/bindings_serialization.h",
+ "lib/bounds_checker.cc",
+ "lib/bounds_checker.h",
+ "lib/buffer.h",
+ "lib/connector.cc",
+ "lib/connector.h",
+ "lib/control_message_handler.cc",
+ "lib/control_message_handler.h",
+ "lib/control_message_proxy.cc",
+ "lib/control_message_proxy.h",
+ "lib/filter_chain.cc",
+ "lib/filter_chain.h",
+ "lib/fixed_buffer.cc",
+ "lib/fixed_buffer.h",
+ "lib/interface_ptr_internal.h",
+ "lib/map_data_internal.h",
+ "lib/map_internal.h",
+ "lib/map_serialization.h",
+ "lib/message.cc",
+ "lib/message_builder.cc",
+ "lib/message_builder.h",
+ "lib/message_filter.cc",
+ "lib/message_header_validator.cc",
+ "lib/message_header_validator.h",
+ "lib/message_internal.h",
+ "lib/no_interface.cc",
+ "lib/router.cc",
+ "lib/router.h",
+ "lib/string_serialization.cc",
+ "lib/string_serialization.h",
+ "lib/union_accessor.h",
+ "lib/validate_params.h",
+ "lib/validation_errors.cc",
+ "lib/validation_errors.h",
+ "lib/validation_util.cc",
+ "lib/validation_util.h",
+ "lib/value_traits.h",
+ "map.h",
+ "message.h",
+ "message_filter.h",
+ "no_interface.h",
+ "string.h",
+ "strong_binding.h",
+ "struct_ptr.h",
+ "type_converter.h",
+ ]
+
+ public_deps = [
+ ":callback",
+ ]
+
+ mojo_sdk_public_deps = [
+ "mojo/public/cpp/system",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/cpp/environment",
+ "mojo/public/interfaces/bindings:bindings_cpp_sources",
+ ]
+}
+
+mojo_sdk_source_set("callback") {
+ sources = [
+ "callback.h",
+ "lib/callback_internal.h",
+ "lib/shared_data.h",
+ "lib/shared_ptr.h",
+ "lib/template_util.h",
+ "lib/thread_checker.h",
+ "lib/thread_checker_posix.cc",
+ "lib/thread_checker_posix.h",
+ ]
+
+ mojo_sdk_deps = [ "mojo/public/cpp/system" ]
+}
diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h
new file mode 100644
index 0000000..f7d3921
--- /dev/null
+++ b/mojo/public/cpp/bindings/array.h
@@ -0,0 +1,249 @@
+// Copyright 2013 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_ARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
+
+#include <string.h>
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#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"
+
+namespace mojo {
+
+// Represents a moveable array with contents of type |T|. The array can be null,
+// meaning that no value has been assigned to it. Null is distinct from empty.
+template <typename T>
+class Array {
+ MOJO_MOVE_ONLY_TYPE(Array)
+ public:
+ typedef internal::ArrayTraits<T, internal::IsMoveOnlyType<T>::value> Traits;
+ typedef typename Traits::ConstRefType ConstRefType;
+ typedef typename Traits::RefType RefType;
+ typedef typename Traits::StorageType StorageType;
+ typedef typename Traits::ForwardType ForwardType;
+
+ typedef internal::Array_Data<typename internal::WrapperTraits<T>::DataType>
+ Data_;
+
+ // Constructs a new array that is null.
+ Array() : 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 Array(size_t size) : vec_(size), is_null_(false) {
+ Traits::Initialize(&vec_);
+ }
+ ~Array() { Traits::Finalize(&vec_); }
+
+ // Moves the contents of |other| into this array.
+ Array(Array&& other) : is_null_(true) { Take(&other); }
+ Array& operator=(Array&& other) {
+ Take(&other);
+ 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 Array New(size_t size) { return Array(size).Pass(); }
+
+ // Creates a new array with a copy of the contents of |other|.
+ template <typename U>
+ static Array From(const U& other) {
+ return TypeConverter<Array, 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, Array>::Convert(*this);
+ }
+
+ // Resets the contents of this array back to null.
+ void reset() {
+ if (!vec_.empty()) {
+ Traits::Finalize(&vec_);
+ vec_.clear();
+ }
+ is_null_ = true;
+ }
+
+ // Indicates whether the array is null (which is distinct from empty).
+ bool is_null() const { return is_null_; }
+
+ // Returns a reference to the first element of the array. Calling this on a
+ // null or empty array causes undefined behavior.
+ ConstRefType front() const { return vec_.front(); }
+ RefType front() { return vec_.front(); }
+
+ // 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.
+ ConstRefType at(size_t offset) const { return Traits::at(&vec_, offset); }
+ ConstRefType operator[](size_t offset) const { return at(offset); }
+ RefType at(size_t offset) { return Traits::at(&vec_, offset); }
+ RefType operator[](size_t offset) { return at(offset); }
+
+ // Pushes |value| onto the back of the array. If this array was null, it will
+ // become non-null with a size of 1.
+ void push_back(ForwardType value) {
+ is_null_ = false;
+ Traits::PushBack(&vec_, value);
+ }
+
+ // Resizes the array to |size| and makes it non-null. Otherwise, works just
+ // like the resize method of |std::vector|.
+ void resize(size_t size) {
+ is_null_ = false;
+ Traits::Resize(&vec_, size);
+ }
+
+ // Returns a const reference to the |std::vector| managed by this class. If
+ // the array is null, this will be an empty vector.
+ const std::vector<StorageType>& storage() const { return vec_; }
+ operator const std::vector<StorageType>&() const { return vec_; }
+
+ // Swaps the contents of this array with the |other| array, including
+ // nullness.
+ void Swap(Array* 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(std::vector<StorageType>* 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).
+ Array Clone() const {
+ Array result;
+ result.is_null_ = is_null_;
+ Traits::Clone(vec_, &result.vec_);
+ return result.Pass();
+ }
+
+ // 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 Array& 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 std::vector<StorageType> Array::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &Array::vec_; }
+
+ private:
+ // Forbid the == and != operators explicitly, otherwise Array will be
+ // converted to Testable to do == or != comparison.
+ template <typename U>
+ bool operator==(const Array<U>& other) const = delete;
+ template <typename U>
+ bool operator!=(const Array<U>& other) const = delete;
+
+ void Take(Array* other) {
+ reset();
+ Swap(other);
+ }
+
+ std::vector<StorageType> vec_;
+ bool is_null_;
+};
+
+// A |TypeConverter| that will create an |Array<T>| containing a copy of the
+// contents of an |std::vector<E>|, using |TypeConverter<T, E>| to copy each
+// element. The returned array will always be non-null.
+template <typename T, typename E>
+struct TypeConverter<Array<T>, std::vector<E>> {
+ static Array<T> Convert(const std::vector<E>& input) {
+ Array<T> result(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ result[i] = TypeConverter<T, E>::Convert(input[i]);
+ return result.Pass();
+ }
+};
+
+// A |TypeConverter| that will create an |std::vector<E>| containing a copy of
+// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each
+// element. If the input array is null, the output vector will be empty.
+template <typename E, typename T>
+struct TypeConverter<std::vector<E>, Array<T>> {
+ static std::vector<E> Convert(const Array<T>& input) {
+ std::vector<E> result;
+ if (!input.is_null()) {
+ result.resize(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ result[i] = TypeConverter<E, T>::Convert(input[i]);
+ }
+ return result;
+ }
+};
+
+// A |TypeConverter| that will create an |Array<T>| containing a copy of the
+// contents of an |std::set<E>|, using |TypeConverter<T, E>| to copy each
+// element. The returned array will always be non-null.
+template <typename T, typename E>
+struct TypeConverter<Array<T>, std::set<E>> {
+ static Array<T> Convert(const std::set<E>& input) {
+ Array<T> result(0u);
+ for (auto i : input)
+ result.push_back(TypeConverter<T, E>::Convert(i));
+ return result.Pass();
+ }
+};
+
+// A |TypeConverter| that will create an |std::set<E>| containing a copy of
+// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each
+// element. If the input array is null, the output set will be empty.
+template <typename E, typename T>
+struct TypeConverter<std::set<E>, Array<T>> {
+ static std::set<E> Convert(const Array<T>& input) {
+ std::set<E> result;
+ if (!input.is_null()) {
+ for (size_t i = 0; i < input.size(); ++i)
+ result.insert(TypeConverter<E, T>::Convert(input[i]));
+ }
+ return result;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
new file mode 100644
index 0000000..55f9c4a
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
@@ -0,0 +1,32 @@
+// Copyright 2015 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_ASSOCIATED_INTERFACE_PTR_INFO_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// AssociatedInterfacePtrInfo stores necessary information to construct an
+// associated interface pointer.
+// TODO(yzshen): implement it.
+template <typename Interface>
+class AssociatedInterfacePtrInfo {
+ MOJO_MOVE_ONLY_TYPE(AssociatedInterfacePtrInfo);
+
+ public:
+ AssociatedInterfacePtrInfo() {}
+ AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other) {}
+
+ AssociatedInterfacePtrInfo& operator=(AssociatedInterfacePtrInfo&& other) {
+ return *this;
+ }
+
+ bool is_valid() const { return false; }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_INFO_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_request.h b/mojo/public/cpp/bindings/associated_interface_request.h
new file mode 100644
index 0000000..9882b89
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_interface_request.h
@@ -0,0 +1,31 @@
+// Copyright 2015 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_ASSOCIATED_INTERFACE_REQUEST_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// Represents an associated interface request.
+// TODO(yzshen): implement it.
+template <typename Interface>
+class AssociatedInterfaceRequest {
+ MOJO_MOVE_ONLY_TYPE(AssociatedInterfaceRequest)
+
+ public:
+ AssociatedInterfaceRequest() {}
+ AssociatedInterfaceRequest(AssociatedInterfaceRequest&& other) {}
+
+ AssociatedInterfaceRequest& operator=(AssociatedInterfaceRequest&& other) {
+ return *this;
+ }
+
+ bool is_pending() const { return false; }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
new file mode 100644
index 0000000..de9159f
--- /dev/null
+++ b/mojo/public/cpp/bindings/binding.h
@@ -0,0 +1,239 @@
+// Copyright 2014 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_BINDING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// Represents the binding of an interface implementation to a message pipe.
+// When the |Binding| object is destroyed, the binding between the message pipe
+// and the interface is torn down and the message pipe is closed, leaving the
+// interface implementation in an unbound state.
+//
+// Example:
+//
+// #include "foo.mojom.h"
+//
+// class FooImpl : public Foo {
+// public:
+// explicit FooImpl(InterfaceRequest<Foo> request)
+// : binding_(this, request.Pass()) {}
+//
+// // Foo implementation here.
+//
+// private:
+// Binding<Foo> binding_;
+// };
+//
+// class MyFooFactory : public InterfaceFactory<Foo> {
+// public:
+// void Create(..., InterfaceRequest<Foo> request) override {
+// auto f = new FooImpl(request.Pass());
+// // Do something to manage the lifetime of |f|. Use StrongBinding<> to
+// // delete FooImpl on connection errors.
+// }
+// };
+//
+// The caller may specify a |MojoAsyncWaiter| to be used by the connection when
+// waiting for calls to arrive. Normally it is fine to use the default waiter.
+// However, the caller may provide their own implementation if needed. The
+// |Binding| will not take ownership of the waiter, and the waiter must outlive
+// the |Binding|. The provided waiter must be able to signal the implementation
+// which generally means it needs to be able to schedule work on the thread the
+// implementation runs on. If writing library code that has to work on different
+// types of threads callers may need to provide different waiter
+// implementations.
+template <typename Interface>
+class Binding {
+ public:
+ // Constructs an incomplete binding that will use the implementation |impl|.
+ // The binding may be completed with a subsequent call to the |Bind| method.
+ // Does not take ownership of |impl|, which must outlive the binding.
+ explicit Binding(Interface* impl) : impl_(impl) { stub_.set_sink(impl_); }
+
+ // Constructs a completed binding of message pipe |handle| to implementation
+ // |impl|. Does not take ownership of |impl|, which must outlive the binding.
+ // See class comment for definition of |waiter|.
+ Binding(Interface* impl,
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter())
+ : Binding(impl) {
+ Bind(handle.Pass(), waiter);
+ }
+
+ // Constructs a completed binding of |impl| to a new message pipe, passing the
+ // client end to |ptr|, which takes ownership of it. The caller is expected to
+ // pass |ptr| on to the client of the service. Does not take ownership of any
+ // of the parameters. |impl| must outlive the binding. |ptr| only needs to
+ // last until the constructor returns. See class comment for definition of
+ // |waiter|.
+ Binding(Interface* impl,
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter())
+ : Binding(impl) {
+ Bind(ptr, waiter);
+ }
+
+ // Constructs a completed binding of |impl| to the message pipe endpoint in
+ // |request|, taking ownership of the endpoint. Does not take ownership of
+ // |impl|, which must outlive the binding. See class comment for definition of
+ // |waiter|.
+ Binding(Interface* impl,
+ InterfaceRequest<Interface> request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter())
+ : Binding(impl) {
+ Bind(request.PassMessagePipe(), waiter);
+ }
+
+ // Tears down the binding, closing the message pipe and leaving the interface
+ // implementation unbound.
+ ~Binding() {
+ if (internal_router_)
+ Close();
+ }
+
+ // Completes a binding that was constructed with only an interface
+ // implementation. Takes ownership of |handle| and binds it to the previously
+ // specified implementation. See class comment for definition of |waiter|.
+ void Bind(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ MOJO_DCHECK(!internal_router_);
+ internal::FilterChain filters;
+ filters.Append<internal::MessageHeaderValidator>();
+ filters.Append<typename Interface::RequestValidator_>();
+
+ internal_router_ =
+ new internal::Router(handle.Pass(), filters.Pass(), waiter);
+ internal_router_->set_incoming_receiver(&stub_);
+ internal_router_->set_connection_error_handler(
+ [this]() { connection_error_handler_.Run(); });
+ }
+
+ // Completes a binding that was constructed with only an interface
+ // implementation by creating a new message pipe, binding one end of it to the
+ // previously specified implementation, and passing the other to |ptr|, which
+ // takes ownership of it. The caller is expected to pass |ptr| on to the
+ // eventual client of the service. Does not take ownership of |ptr|. See
+ // class comment for definition of |waiter|.
+ void Bind(
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ MessagePipe pipe;
+ ptr->Bind(
+ InterfacePtrInfo<Interface>(pipe.handle0.Pass(), Interface::Version_),
+ waiter);
+ Bind(pipe.handle1.Pass(), waiter);
+ }
+
+ // Completes a binding that was constructed with only an interface
+ // implementation by removing the message pipe endpoint from |request| and
+ // binding it to the previously specified implementation. See class comment
+ // for definition of |waiter|.
+ void Bind(
+ InterfaceRequest<Interface> request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ Bind(request.PassMessagePipe(), waiter);
+ }
+
+ // Stops processing incoming messages until
+ // ResumeIncomingMethodCallProcessing(), or WaitForIncomingMethodCall().
+ // Outgoing messages are still sent.
+ //
+ // No errors are detected on the message pipe while paused.
+ void PauseIncomingMethodCallProcessing() {
+ MOJO_DCHECK(internal_router_);
+ internal_router_->PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ MOJO_DCHECK(internal_router_);
+ internal_router_->ResumeIncomingMethodCallProcessing();
+ }
+
+ // Blocks the calling thread until either a call arrives on the previously
+ // bound message pipe, the deadline is exceeded, or an error occurs. Returns
+ // true if a method was successfully read and dispatched.
+ bool WaitForIncomingMethodCall(
+ MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+ MOJO_DCHECK(internal_router_);
+ return internal_router_->WaitForIncomingMessage(deadline);
+ }
+
+ // Closes the message pipe that was previously bound. Put this object into a
+ // state where it can be rebound to a new pipe.
+ void Close() {
+ MOJO_DCHECK(internal_router_);
+ internal_router_->CloseMessagePipe();
+ DestroyRouter();
+ }
+
+ // Unbinds the underlying pipe from this binding and returns it so it can be
+ // used in another context, such as on another thread or with a different
+ // implementation. Put this object into a state where it can be rebound to a
+ // new pipe.
+ InterfaceRequest<Interface> Unbind() {
+ InterfaceRequest<Interface> request =
+ MakeRequest<Interface>(internal_router_->PassMessagePipe());
+ DestroyRouter();
+ // TODO(vtl): The |.Pass()| below is only needed due to an MSVS bug; remove
+ // it once that's fixed.
+ return request.Pass();
+ }
+
+ // Sets an error handler that will be called if a connection error occurs on
+ // the bound message pipe.
+ void set_connection_error_handler(const Closure& error_handler) {
+ connection_error_handler_ = error_handler;
+ }
+
+ // Returns the interface implementation that was previously specified. Caller
+ // does not take ownership.
+ Interface* impl() { return impl_; }
+
+ // Indicates whether the binding has been completed (i.e., whether a message
+ // pipe has been bound to the implementation).
+ bool is_bound() const { return !!internal_router_; }
+
+ // Returns the value of the handle currently bound to this Binding which can
+ // be used to make explicit Wait/WaitMany calls. Requires that the Binding be
+ // bound. Ownership of the handle is retained by the Binding, it is not
+ // transferred to the caller.
+ MessagePipeHandle handle() const {
+ MOJO_DCHECK(is_bound());
+ return internal_router_->handle();
+ }
+
+ // Exposed for testing, should not generally be used.
+ internal::Router* internal_router() { return internal_router_; }
+
+ private:
+ void DestroyRouter() {
+ internal_router_->set_connection_error_handler(Closure());
+ delete internal_router_;
+ internal_router_ = nullptr;
+ }
+
+ internal::Router* internal_router_ = nullptr;
+ typename Interface::Stub_ stub_;
+ Interface* impl_;
+ Closure connection_error_handler_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Binding);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
diff --git a/mojo/public/cpp/bindings/callback.h b/mojo/public/cpp/bindings/callback.h
new file mode 100644
index 0000000..beec1a1
--- /dev/null
+++ b/mojo/public/cpp/bindings/callback.h
@@ -0,0 +1,115 @@
+// Copyright 2014 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_CALLBACK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+
+#include "mojo/public/cpp/bindings/lib/callback_internal.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+
+template <typename Sig>
+class Callback;
+
+// Represents a callback with any number of parameters and no return value. The
+// callback is executed by calling its Run() method. The callback may be "null",
+// meaning it does nothing.
+template <typename... Args>
+class Callback<void(Args...)> {
+ public:
+ // An interface that may be implemented to define the Run() method.
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ // ForwardType ensures String is passed as a const reference.
+ typename internal::Callback_ParamTraits<Args>::ForwardType...)
+ const = 0;
+ };
+
+ // Constructs a "null" callback that does nothing.
+ Callback() {}
+
+ // Constructs a callback that will run |runnable|. The callback takes
+ // ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // As above, but can take an object that isn't derived from Runnable, so long
+ // as it has a compatible operator() or Run() method. operator() will be
+ // preferred if the type has both.
+ template <typename Sink>
+ Callback(const Sink& sink) {
+ using sink_type = typename internal::Conditional<
+ internal::HasCompatibleCallOperator<Sink, Args...>::value,
+ FunctorAdapter<Sink>, RunnableAdapter<Sink>>::type;
+ sink_ = internal::SharedPtr<Runnable>(new sink_type(sink));
+ }
+
+ // As above, but can take a compatible function pointer.
+ Callback(void (*function_ptr)(
+ typename internal::Callback_ParamTraits<Args>::ForwardType...))
+ : sink_(new FunctionPtrAdapter(function_ptr)) {}
+
+ // Executes the callback function, invoking Pass() on move-only types.
+ void Run(typename internal::Callback_ParamTraits<Args>::ForwardType... args)
+ const {
+ if (sink_.get())
+ sink_->Run(internal::Forward(args)...);
+ }
+
+ bool is_null() const { return !sink_.get(); }
+
+ // Resets the callback to the "null" state.
+ void reset() { sink_.reset(); }
+
+ private:
+ // Adapts a class that has a Run() method but is not derived from Runnable to
+ // be callable by Callback.
+ template <typename Sink>
+ struct RunnableAdapter : public Runnable {
+ explicit RunnableAdapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<Args>::ForwardType... args)
+ const override {
+ sink.Run(internal::Forward(args)...);
+ }
+ Sink sink;
+ };
+
+ // Adapts a class that has a compatible operator() to be callable by Callback.
+ template <typename Sink>
+ struct FunctorAdapter : public Runnable {
+ explicit FunctorAdapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<Args>::ForwardType... args)
+ const override {
+ sink.operator()(internal::Forward(args)...);
+ }
+ Sink sink;
+ };
+
+ // Adapts a function pointer.
+ struct FunctionPtrAdapter : public Runnable {
+ explicit FunctionPtrAdapter(void (*function_ptr)(
+ typename internal::Callback_ParamTraits<Args>::ForwardType...))
+ : function_ptr(function_ptr) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<Args>::ForwardType... args)
+ const override {
+ (*function_ptr)(internal::Forward(args)...);
+ }
+ void (*function_ptr)(
+ typename internal::Callback_ParamTraits<Args>::ForwardType...);
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+// A specialization of Callback which takes no parameters.
+typedef Callback<void()> Closure;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
new file mode 100644
index 0000000..dc44667
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -0,0 +1,197 @@
+// Copyright 2014 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_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+
+#include <algorithm>
+
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/lib/interface_ptr_internal.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// A pointer to a local proxy of a remote Interface implementation. Uses a
+// message pipe to communicate with the remote implementation, and automatically
+// closes the pipe and deletes the proxy on destruction. The pointer must be
+// bound to a message pipe before the interface methods can be called.
+//
+// This class is thread hostile, as is the local proxy it manages. All calls to
+// this class or the proxy should be from the same thread that created it. If
+// you need to move the proxy to a different thread, extract the
+// InterfacePtrInfo (containing just the message pipe and any version
+// information) using PassInterface(), pass it to a different thread, and
+// create and bind a new InterfacePtr from that thread.
+template <typename Interface>
+class InterfacePtr {
+ MOJO_MOVE_ONLY_TYPE(InterfacePtr)
+ public:
+ // Constructs an unbound InterfacePtr.
+ InterfacePtr() {}
+ InterfacePtr(decltype(nullptr)) {}
+
+ // Takes over the binding of another InterfacePtr.
+ InterfacePtr(InterfacePtr&& other) {
+ internal_state_.Swap(&other.internal_state_);
+ }
+
+ // Takes over the binding of another InterfacePtr, and closes any message pipe
+ // already bound to this pointer.
+ InterfacePtr& operator=(InterfacePtr&& other) {
+ reset();
+ internal_state_.Swap(&other.internal_state_);
+ return *this;
+ }
+
+ // Assigning nullptr to this class causes it to close the currently bound
+ // message pipe (if any) and returns the pointer to the unbound state.
+ InterfacePtr& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ // Closes the bound message pipe (if any) on destruction.
+ ~InterfacePtr() {}
+
+ // Binds the InterfacePtr to a remote implementation of Interface. The
+ // |waiter| is used for receiving notifications when there is data to read
+ // from the message pipe. For most callers, the default |waiter| will be
+ // sufficient.
+ //
+ // Calling with an invalid |info| (containing an invalid message pipe handle)
+ // has the same effect as reset(). In this case, the InterfacePtr is not
+ // considered as bound.
+ void Bind(
+ InterfacePtrInfo<Interface> info,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ reset();
+ if (info.is_valid())
+ internal_state_.Bind(info.Pass(), waiter);
+ }
+
+ // Returns whether or not this InterfacePtr is bound to a message pipe.
+ bool is_bound() const { return internal_state_.is_bound(); }
+
+ // Returns a raw pointer to the local proxy. Caller does not take ownership.
+ // Note that the local proxy is thread hostile, as stated above.
+ Interface* get() const { return internal_state_.instance(); }
+
+ // Functions like a pointer to Interface. Must already be bound.
+ Interface* operator->() const { return get(); }
+ Interface& operator*() const { return *get(); }
+
+ // Returns the version number of the interface that the remote side supports.
+ uint32_t version() const { return internal_state_.version(); }
+
+ // Queries the max version that the remote side supports. On completion, the
+ // result will be returned as the input of |callback|. The version number of
+ // this interface pointer will also be updated.
+ void QueryVersion(const Callback<void(uint32_t)>& callback) {
+ internal_state_.QueryVersion(callback);
+ }
+
+ // If the remote side doesn't support the specified version, it will close its
+ // end of the message pipe asynchronously. This does nothing if it's already
+ // known that the remote side supports the specified version, i.e., if
+ // |version <= this->version()|.
+ //
+ // After calling RequireVersion() with a version not supported by the remote
+ // side, all subsequent calls to interface methods will be ignored.
+ void RequireVersion(uint32_t version) {
+ internal_state_.RequireVersion(version);
+ }
+
+ // Closes the bound message pipe (if any) and returns the pointer to the
+ // unbound state.
+ void reset() {
+ State doomed;
+ internal_state_.Swap(&doomed);
+ }
+
+ // Blocks the current thread until the next incoming response callback arrives
+ // or an error occurs. Returns |true| if a response arrived, or |false| in
+ // case of error.
+ //
+ // This method may only be called after the InterfacePtr has been bound to a
+ // message pipe.
+ bool WaitForIncomingResponse() {
+ return internal_state_.WaitForIncomingResponse();
+ }
+
+ // Indicates whether the message pipe has encountered an error. If true,
+ // method calls made on this interface will be dropped (and may already have
+ // been dropped).
+ bool encountered_error() const { return internal_state_.encountered_error(); }
+
+ // Registers a handler to receive error notifications. The handler will be
+ // called from the thread that owns this InterfacePtr.
+ //
+ // This method may only be called after the InterfacePtr has been bound to a
+ // message pipe.
+ void set_connection_error_handler(const Closure& error_handler) {
+ internal_state_.set_connection_error_handler(error_handler);
+ }
+
+ // Unbinds the InterfacePtr and returns the information which could be used
+ // to setup an InterfacePtr again. This method may be used to move the proxy
+ // to a different thread (see class comments for details).
+ //
+ // It is an error to call PassInterface() while there are pending responses.
+ // TODO: fix this restriction, it's not always obvious when there is a
+ // pending response.
+ InterfacePtrInfo<Interface> PassInterface() {
+ MOJO_DCHECK(!internal_state_.has_pending_callbacks());
+ State state;
+ internal_state_.Swap(&state);
+
+ return state.PassInterface();
+ }
+
+ // DO NOT USE. Exposed only for internal use and for testing.
+ internal::InterfacePtrState<Interface>* internal_state() {
+ return &internal_state_;
+ }
+
+ // Allow InterfacePtr<> to be used in boolean expressions, but not
+ // implicitly convertible to a real bool (which is dangerous).
+ private:
+ typedef internal::InterfacePtrState<Interface> InterfacePtr::*Testable;
+
+ public:
+ operator Testable() const {
+ return internal_state_.is_bound() ? &InterfacePtr::internal_state_
+ : nullptr;
+ }
+
+ private:
+ // Forbid the == and != operators explicitly, otherwise InterfacePtr will be
+ // converted to Testable to do == or != comparison.
+ template <typename T>
+ bool operator==(const InterfacePtr<T>& other) const = delete;
+ template <typename T>
+ bool operator!=(const InterfacePtr<T>& other) const = delete;
+
+ typedef internal::InterfacePtrState<Interface> State;
+ mutable State internal_state_;
+};
+
+// If |info| is valid (containing a valid message pipe handle), returns an
+// InterfacePtr bound to it. Otherwise, returns an unbound InterfacePtr. The
+// specified |waiter| will be used as in the InterfacePtr::Bind() method.
+template <typename Interface>
+InterfacePtr<Interface> MakeProxy(
+ InterfacePtrInfo<Interface> info,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ InterfacePtr<Interface> ptr;
+ if (info.is_valid())
+ ptr.Bind(info.Pass(), waiter);
+ return ptr.Pass();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/interface_ptr_info.h b/mojo/public/cpp/bindings/interface_ptr_info.h
new file mode 100644
index 0000000..4f61915
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr_info.h
@@ -0,0 +1,58 @@
+// Copyright 2015 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_INTERFACE_PTR_INFO_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace mojo {
+
+// InterfacePtrInfo stores necessary information to communicate with a remote
+// interface implementation, which could be used to construct an InterfacePtr.
+template <typename Interface>
+class InterfacePtrInfo {
+ MOJO_MOVE_ONLY_TYPE(InterfacePtrInfo);
+
+ public:
+ InterfacePtrInfo() : version_(0u) {}
+
+ InterfacePtrInfo(ScopedMessagePipeHandle handle, uint32_t version)
+ : handle_(handle.Pass()), version_(version) {}
+
+ InterfacePtrInfo(InterfacePtrInfo&& other)
+ : handle_(other.handle_.Pass()), version_(other.version_) {
+ other.version_ = 0u;
+ }
+
+ ~InterfacePtrInfo() {}
+
+ InterfacePtrInfo& operator=(InterfacePtrInfo&& other) {
+ if (this != &other) {
+ handle_ = other.handle_.Pass();
+ version_ = other.version_;
+ other.version_ = 0u;
+ }
+
+ return *this;
+ }
+
+ bool is_valid() const { return handle_.is_valid(); }
+
+ ScopedMessagePipeHandle PassHandle() { return handle_.Pass(); }
+ const ScopedMessagePipeHandle& handle() const { return handle_; }
+ void set_handle(ScopedMessagePipeHandle handle) { handle_ = handle.Pass(); }
+
+ uint32_t version() const { return version_; }
+ void set_version(uint32_t version) { version_ = version; }
+
+ private:
+ ScopedMessagePipeHandle handle_;
+ uint32_t version_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_INFO_H_
diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h
new file mode 100644
index 0000000..c139306
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_request.h
@@ -0,0 +1,118 @@
+// Copyright 2014 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_INTERFACE_REQUEST_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+
+namespace mojo {
+
+// Represents a request from a remote client for an implementation of Interface
+// over a specified message pipe. The implementor of the interface should
+// remove the message pipe by calling PassMessagePipe() and bind it to the
+// implementation. If this is not done, the InterfaceRequest will automatically
+// close the pipe on destruction. Can also represent the absence of a request
+// if the client did not provide a message pipe.
+template <typename Interface>
+class InterfaceRequest {
+ MOJO_MOVE_ONLY_TYPE(InterfaceRequest)
+ public:
+ // Constructs an empty InterfaceRequest, representing that the client is not
+ // requesting an implementation of Interface.
+ InterfaceRequest() {}
+ InterfaceRequest(decltype(nullptr)) {}
+
+ // Takes the message pipe from another InterfaceRequest.
+ InterfaceRequest(InterfaceRequest&& other) { handle_ = other.handle_.Pass(); }
+ InterfaceRequest& operator=(InterfaceRequest&& other) {
+ handle_ = other.handle_.Pass();
+ return *this;
+ }
+
+ // Assigning to nullptr resets the InterfaceRequest to an empty state,
+ // closing the message pipe currently bound to it (if any).
+ InterfaceRequest& operator=(decltype(nullptr)) {
+ handle_.reset();
+ return *this;
+ }
+
+ // Binds the request to a message pipe over which Interface is to be
+ // requested. If the request is already bound to a message pipe, the current
+ // message pipe will be closed.
+ void Bind(ScopedMessagePipeHandle handle) { handle_ = handle.Pass(); }
+
+ // Indicates whether the request currently contains a valid message pipe.
+ bool is_pending() const { return handle_.is_valid(); }
+
+ // Removes the message pipe from the request and returns it.
+ ScopedMessagePipeHandle PassMessagePipe() { return handle_.Pass(); }
+
+ private:
+ ScopedMessagePipeHandle handle_;
+};
+
+// Makes an InterfaceRequest bound to the specified message pipe. If |handle|
+// is empty or invalid, the resulting InterfaceRequest will represent the
+// absence of a request.
+template <typename Interface>
+InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) {
+ InterfaceRequest<Interface> request;
+ request.Bind(handle.Pass());
+ return request.Pass();
+}
+
+// Creates a new message pipe over which Interface is to be served. Binds the
+// specified InterfacePtr to one end of the message pipe, and returns an
+// InterfaceRequest bound to the other. The InterfacePtr should be passed to
+// the client, and the InterfaceRequest should be passed to whatever will
+// provide the implementation. The implementation should typically be bound to
+// the InterfaceRequest using the Binding or StrongBinding classes. The client
+// may begin to issue calls even before an implementation has been bound, since
+// messages sent over the pipe will just queue up until they are consumed by
+// the implementation.
+//
+// Example #1: Requesting a remote implementation of an interface.
+// ===============================================================
+//
+// Given the following interface:
+//
+// interface Database {
+// OpenTable(Table& table);
+// }
+//
+// The client would have code similar to the following:
+//
+// DatabasePtr database = ...; // Connect to database.
+// TablePtr table;
+// database->OpenTable(GetProxy(&table));
+//
+// Upon return from GetProxy, |table| is ready to have methods called on it.
+//
+// Example #2: Registering a local implementation with a remote service.
+// =====================================================================
+//
+// Given the following interface
+// interface Collector {
+// RegisterSource(Source source);
+// }
+//
+// The client would have code similar to the following:
+//
+// CollectorPtr collector = ...; // Connect to Collector.
+// SourcePtr source;
+// InterfaceRequest<Source> source_request = GetProxy(&source);
+// collector->RegisterSource(source.Pass());
+// CreateSource(source_request.Pass()); // Create implementation locally.
+//
+template <typename Interface>
+InterfaceRequest<Interface> GetProxy(InterfacePtr<Interface>* ptr) {
+ MessagePipe pipe;
+ ptr->Bind(InterfacePtrInfo<Interface>(pipe.handle0.Pass(), 0u));
+ return MakeRequest<Interface>(pipe.handle1.Pass());
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
diff --git a/mojo/public/cpp/bindings/lib/TODO b/mojo/public/cpp/bindings/lib/TODO
new file mode 100644
index 0000000..ea4ce81
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/TODO
@@ -0,0 +1,4 @@
+TODOs:
+ - Optimize Buffer classes?
+ - Add compile-time asserts to verify object packing and padding.
+ - Investigate making arrays of objects not be arrays of pointers.
diff --git a/mojo/public/cpp/bindings/lib/array_internal.cc b/mojo/public/cpp/bindings/lib/array_internal.cc
new file mode 100644
index 0000000..61e4b0d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.cc
@@ -0,0 +1,74 @@
+// Copyright 2013 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/lib/array_internal.h"
+
+#include <sstream>
+
+namespace mojo {
+namespace internal {
+
+std::string MakeMessageWithArrayIndex(const char* message,
+ size_t size,
+ size_t index) {
+ std::ostringstream stream;
+ stream << message << ": array size - " << size << "; index - " << index;
+ return stream.str();
+}
+
+std::string MakeMessageWithExpectedArraySize(const char* message,
+ size_t size,
+ size_t expected_size) {
+ std::ostringstream stream;
+ stream << message << ": array size - " << size << "; expected size - "
+ << expected_size;
+ return stream.str();
+}
+
+ArrayDataTraits<bool>::BitRef::~BitRef() {
+}
+
+ArrayDataTraits<bool>::BitRef::BitRef(uint8_t* storage, uint8_t mask)
+ : storage_(storage), mask_(mask) {
+}
+
+ArrayDataTraits<bool>::BitRef& ArrayDataTraits<bool>::BitRef::operator=(
+ bool value) {
+ if (value) {
+ *storage_ |= mask_;
+ } else {
+ *storage_ &= ~mask_;
+ }
+ return *this;
+}
+
+ArrayDataTraits<bool>::BitRef& ArrayDataTraits<bool>::BitRef::operator=(
+ const BitRef& value) {
+ return (*this) = static_cast<bool>(value);
+}
+
+ArrayDataTraits<bool>::BitRef::operator bool() const {
+ return (*storage_ & mask_) != 0;
+}
+
+// static
+void ArraySerializationHelper<Handle, true>::EncodePointersAndHandles(
+ const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ EncodeHandle(&elements[i], handles);
+}
+
+// static
+void ArraySerializationHelper<Handle, true>::DecodePointersAndHandles(
+ const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ DecodeHandle(&elements[i], handles);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h
new file mode 100644
index 0000000..41ca174
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -0,0 +1,534 @@
+// Copyright 2013 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_ARRAY_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
+
+#include <new>
+#include <vector>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+template <typename T>
+class Array;
+class String;
+
+namespace internal {
+
+// std::numeric_limits<uint32_t>::max() is not a compile-time constant (until
+// C++11).
+const uint32_t kMaxUint32 = 0xFFFFFFFF;
+
+std::string MakeMessageWithArrayIndex(const char* message,
+ size_t size,
+ size_t index);
+
+std::string MakeMessageWithExpectedArraySize(const char* message,
+ size_t size,
+ size_t expected_size);
+
+template <typename T>
+struct ArrayDataTraits {
+ typedef T StorageType;
+ typedef T& Ref;
+ typedef T const& ConstRef;
+
+ static const uint32_t kMaxNumElements =
+ (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType);
+
+ static uint32_t GetStorageSize(uint32_t num_elements) {
+ MOJO_DCHECK(num_elements <= kMaxNumElements);
+ return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
+ }
+ static Ref ToRef(StorageType* storage, size_t offset) {
+ return storage[offset];
+ }
+ static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+ return storage[offset];
+ }
+};
+
+template <typename P>
+struct ArrayDataTraits<P*> {
+ typedef StructPointer<P> StorageType;
+ typedef P*& Ref;
+ typedef P* const& ConstRef;
+
+ static const uint32_t kMaxNumElements =
+ (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType);
+
+ static uint32_t GetStorageSize(uint32_t num_elements) {
+ MOJO_DCHECK(num_elements <= kMaxNumElements);
+ return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
+ }
+ static Ref ToRef(StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+ static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+};
+
+template <typename T>
+struct ArrayDataTraits<Array_Data<T>*> {
+ typedef ArrayPointer<T> StorageType;
+ typedef Array_Data<T>*& Ref;
+ typedef Array_Data<T>* const& ConstRef;
+
+ static const uint32_t kMaxNumElements =
+ (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType);
+
+ static uint32_t GetStorageSize(uint32_t num_elements) {
+ MOJO_DCHECK(num_elements <= kMaxNumElements);
+ return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
+ }
+ static Ref ToRef(StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+ static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+};
+
+// Specialization of Arrays for bools, optimized for space. It has the
+// following differences from a generalized Array:
+// * Each element takes up a single bit of memory.
+// * Accessing a non-const single element uses a helper class |BitRef|, which
+// emulates a reference to a bool.
+template <>
+struct ArrayDataTraits<bool> {
+ // Helper class to emulate a reference to a bool, used for direct element
+ // access.
+ class BitRef {
+ public:
+ ~BitRef();
+ BitRef& operator=(bool value);
+ BitRef& operator=(const BitRef& value);
+ operator bool() const;
+
+ private:
+ friend struct ArrayDataTraits<bool>;
+ BitRef(uint8_t* storage, uint8_t mask);
+ BitRef();
+ uint8_t* storage_;
+ uint8_t mask_;
+ };
+
+ // Because each element consumes only 1/8 byte.
+ static const uint32_t kMaxNumElements = kMaxUint32;
+
+ typedef uint8_t StorageType;
+ typedef BitRef Ref;
+ typedef bool ConstRef;
+
+ static uint32_t GetStorageSize(uint32_t num_elements) {
+ return sizeof(ArrayHeader) + ((num_elements + 7) / 8);
+ }
+ static BitRef ToRef(StorageType* storage, size_t offset) {
+ return BitRef(&storage[offset / 8], 1 << (offset % 8));
+ }
+ static bool ToConstRef(const StorageType* storage, size_t offset) {
+ return (storage[offset / 8] & (1 << (offset % 8))) != 0;
+ }
+};
+
+// What follows is code to support the serialization of Array_Data<T>. There
+// are two interesting cases: arrays of primitives and arrays of objects.
+// Arrays of objects are represented as arrays of pointers to objects.
+
+template <typename T, bool is_handle>
+struct ArraySerializationHelper;
+
+template <typename T>
+struct ArraySerializationHelper<T, false> {
+ typedef typename ArrayDataTraits<T>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {}
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {}
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params->element_is_nullable)
+ << "Primitive type should be non-nullable";
+ MOJO_DCHECK(!validate_params->element_validate_params)
+ << "Primitive type should not have array validate params";
+ return true;
+ }
+};
+
+template <>
+struct ArraySerializationHelper<Handle, true> {
+ typedef ArrayDataTraits<Handle>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles);
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles);
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params->element_validate_params)
+ << "Handle type should not have array validate params";
+
+ for (uint32_t i = 0; i < header->num_elements; ++i) {
+ if (!validate_params->element_is_nullable &&
+ elements[i].value() == kEncodedInvalidHandleValue) {
+ ReportValidationError(
+ VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ MakeMessageWithArrayIndex(
+ "invalid handle in array expecting valid handles",
+ header->num_elements,
+ i).c_str());
+ return false;
+ }
+ if (!bounds_checker->ClaimHandle(elements[i])) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE);
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+template <typename H>
+struct ArraySerializationHelper<H, true> {
+ typedef typename ArrayDataTraits<H>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ ArraySerializationHelper<Handle, true>::EncodePointersAndHandles(
+ header, elements, handles);
+ }
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ ArraySerializationHelper<Handle, true>::DecodePointersAndHandles(
+ header, elements, handles);
+ }
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ return ArraySerializationHelper<Handle, true>::ValidateElements(
+ header, elements, bounds_checker, validate_params);
+ }
+};
+
+template <typename P>
+struct ArraySerializationHelper<P*, false> {
+ typedef typename ArrayDataTraits<P*>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ Encode(&elements[i], handles);
+ }
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ Decode(&elements[i], handles);
+ }
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ for (uint32_t i = 0; i < header->num_elements; ++i) {
+ if (!validate_params->element_is_nullable && !elements[i].offset) {
+ ReportValidationError(
+ VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ MakeMessageWithArrayIndex("null in array expecting valid pointers",
+ header->num_elements,
+ i).c_str());
+ return false;
+ }
+ if (!ValidateEncodedPointer(&elements[i].offset)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER);
+ return false;
+ }
+ if (!ValidateCaller<P>::Run(DecodePointerRaw(&elements[i].offset),
+ bounds_checker,
+ validate_params->element_validate_params)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ template <typename T,
+ bool is_union = IsUnionDataType<T>::value>
+ struct ValidateCaller {};
+
+ template <typename T>
+ struct ValidateCaller<T, false> {
+ static bool Run(const void* data,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params)
+ << "Struct type should not have array validate params";
+
+ return T::Validate(data, bounds_checker);
+ }
+ };
+
+ template <typename T>
+ struct ValidateCaller<T, true> {
+ static bool Run(const void* data,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params)
+ << "Union type should not have array validate params";
+
+ return T::Validate(data, bounds_checker, true);
+ }
+ };
+
+ template <typename Key, typename Value>
+ struct ValidateCaller<Map_Data<Key, Value>, false> {
+ static bool Run(const void* data,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ return Map_Data<Key, Value>::Validate(data, bounds_checker,
+ validate_params);
+ }
+ };
+
+ template <typename T>
+ struct ValidateCaller<Array_Data<T>, false> {
+ static bool Run(const void* data,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ return Array_Data<T>::Validate(data, bounds_checker, validate_params);
+ }
+ };
+};
+
+template <typename T>
+class Array_Data {
+ public:
+ typedef ArrayDataTraits<T> Traits;
+ typedef typename Traits::StorageType StorageType;
+ typedef typename Traits::Ref Ref;
+ typedef typename Traits::ConstRef ConstRef;
+ typedef ArraySerializationHelper<T, IsHandle<T>::value> Helper;
+
+ // Returns null if |num_elements| or the corresponding storage size cannot be
+ // stored in uint32_t.
+ static Array_Data<T>* New(size_t num_elements, Buffer* buf) {
+ if (num_elements > Traits::kMaxNumElements)
+ return nullptr;
+
+ uint32_t num_bytes =
+ Traits::GetStorageSize(static_cast<uint32_t>(num_elements));
+ return new (buf->Allocate(num_bytes))
+ Array_Data<T>(num_bytes, static_cast<uint32_t>(num_elements));
+ }
+
+ static bool Validate(const void* data,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* validate_params) {
+ if (!data)
+ return true;
+ if (!IsAligned(data)) {
+ ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
+ return false;
+ }
+ if (!bounds_checker->IsValidRange(data, sizeof(ArrayHeader))) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+ const ArrayHeader* header = static_cast<const ArrayHeader*>(data);
+ if (header->num_elements > Traits::kMaxNumElements ||
+ header->num_bytes < Traits::GetStorageSize(header->num_elements)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+ return false;
+ }
+ if (validate_params->expected_num_elements != 0 &&
+ header->num_elements != validate_params->expected_num_elements) {
+ ReportValidationError(
+ VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ MakeMessageWithExpectedArraySize(
+ "fixed-size array has wrong number of elements",
+ header->num_elements,
+ validate_params->expected_num_elements).c_str());
+ return false;
+ }
+ if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+
+ const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data);
+ return Helper::ValidateElements(&object->header_, object->storage(),
+ bounds_checker, validate_params);
+ }
+
+ size_t size() const { return header_.num_elements; }
+
+ Ref at(size_t offset) {
+ MOJO_DCHECK(offset < static_cast<size_t>(header_.num_elements));
+ return Traits::ToRef(storage(), offset);
+ }
+
+ ConstRef at(size_t offset) const {
+ MOJO_DCHECK(offset < static_cast<size_t>(header_.num_elements));
+ return Traits::ToConstRef(storage(), offset);
+ }
+
+ StorageType* storage() {
+ return reinterpret_cast<StorageType*>(reinterpret_cast<char*>(this) +
+ sizeof(*this));
+ }
+
+ const StorageType* storage() const {
+ return reinterpret_cast<const StorageType*>(
+ reinterpret_cast<const char*>(this) + sizeof(*this));
+ }
+
+ void EncodePointersAndHandles(std::vector<Handle>* handles) {
+ Helper::EncodePointersAndHandles(&header_, storage(), handles);
+ }
+
+ void DecodePointersAndHandles(std::vector<Handle>* handles) {
+ Helper::DecodePointersAndHandles(&header_, storage(), handles);
+ }
+
+ private:
+ Array_Data(uint32_t num_bytes, uint32_t num_elements) {
+ header_.num_bytes = num_bytes;
+ header_.num_elements = num_elements;
+ }
+ ~Array_Data() = delete;
+
+ internal::ArrayHeader header_;
+
+ // Elements of type internal::ArrayDataTraits<T>::StorageType follow.
+};
+static_assert(sizeof(Array_Data<char>) == 8, "Bad sizeof(Array_Data)");
+
+// UTF-8 encoded
+typedef Array_Data<char> String_Data;
+
+template <typename T, bool kIsMoveOnlyType>
+struct ArrayTraits {};
+
+template <typename T>
+struct ArrayTraits<T, false> {
+ typedef T StorageType;
+ typedef typename std::vector<T>::reference RefType;
+ typedef typename std::vector<T>::const_reference ConstRefType;
+ typedef ConstRefType ForwardType;
+ static inline void Initialize(std::vector<T>* vec) {}
+ static inline void Finalize(std::vector<T>* vec) {}
+ static inline ConstRefType at(const std::vector<T>* vec, size_t offset) {
+ return vec->at(offset);
+ }
+ static inline RefType at(std::vector<T>* vec, size_t offset) {
+ return vec->at(offset);
+ }
+ static inline void Resize(std::vector<T>* vec, size_t size) {
+ vec->resize(size);
+ }
+ static inline void PushBack(std::vector<T>* vec, ForwardType value) {
+ vec->push_back(value);
+ }
+ static inline void Clone(const std::vector<T>& src_vec,
+ std::vector<T>* dest_vec) {
+ dest_vec->assign(src_vec.begin(), src_vec.end());
+ }
+};
+
+template <typename T>
+struct ArrayTraits<T, true> {
+ struct StorageType {
+ char buf[sizeof(T) + (8 - (sizeof(T) % 8)) % 8]; // Make 8-byte aligned.
+ };
+ typedef T& RefType;
+ typedef const T& ConstRefType;
+ typedef T ForwardType;
+ static inline void Initialize(std::vector<StorageType>* vec) {
+ for (size_t i = 0; i < vec->size(); ++i)
+ new (vec->at(i).buf) T();
+ }
+ static inline void Finalize(std::vector<StorageType>* vec) {
+ for (size_t i = 0; i < vec->size(); ++i)
+ reinterpret_cast<T*>(vec->at(i).buf)->~T();
+ }
+ static inline ConstRefType at(const std::vector<StorageType>* vec,
+ size_t offset) {
+ return *reinterpret_cast<const T*>(vec->at(offset).buf);
+ }
+ static inline RefType at(std::vector<StorageType>* vec, size_t offset) {
+ return *reinterpret_cast<T*>(vec->at(offset).buf);
+ }
+ static inline void Resize(std::vector<StorageType>* vec, size_t size) {
+ size_t old_size = vec->size();
+ for (size_t i = size; i < old_size; i++)
+ reinterpret_cast<T*>(vec->at(i).buf)->~T();
+ ResizeStorage(vec, size);
+ for (size_t i = old_size; i < vec->size(); i++)
+ new (vec->at(i).buf) T();
+ }
+ static inline void PushBack(std::vector<StorageType>* vec, RefType value) {
+ size_t old_size = vec->size();
+ ResizeStorage(vec, old_size + 1);
+ new (vec->at(old_size).buf) T(value.Pass());
+ }
+ static inline void ResizeStorage(std::vector<StorageType>* vec, size_t size) {
+ if (size <= vec->capacity()) {
+ vec->resize(size);
+ return;
+ }
+ std::vector<StorageType> new_storage(size);
+ for (size_t i = 0; i < vec->size(); i++)
+ new (new_storage.at(i).buf) T(at(vec, i).Pass());
+ vec->swap(new_storage);
+ Finalize(&new_storage);
+ }
+ static inline void Clone(const std::vector<StorageType>& src_vec,
+ std::vector<StorageType>* dest_vec) {
+ Resize(dest_vec, src_vec.size());
+ for (size_t i = 0; i < src_vec.size(); ++i)
+ at(dest_vec, i) = at(&src_vec, i).Clone();
+ }
+};
+
+template <>
+struct WrapperTraits<String, false> {
+ typedef String_Data* DataType;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
new file mode 100644
index 0000000..9d0e7bb
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -0,0 +1,338 @@
+// Copyright 2014 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_ARRAY_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
+
+#include <string.h> // For |memcpy()|.
+
+#include <vector>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/map_serialization.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace mojo {
+
+template <typename E>
+inline size_t GetSerializedSize_(const Array<E>& input);
+
+template <typename E, typename F>
+inline void SerializeArray_(
+ Array<E> input,
+ internal::Buffer* buf,
+ internal::Array_Data<F>** output,
+ const internal::ArrayValidateParams* validate_params);
+
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* data, Array<E>* output);
+
+namespace internal {
+
+template <typename E,
+ typename F,
+ bool is_union =
+ IsUnionDataType<typename RemovePointer<F>::type>::value>
+struct ArraySerializer;
+
+// Handles serialization and deserialization of arrays of pod types.
+template <typename E, typename F>
+struct ArraySerializer<E, F, false> {
+ static_assert(sizeof(E) == sizeof(F), "Incorrect array serializer");
+ static size_t GetSerializedSize(const Array<E>& input) {
+ return sizeof(Array_Data<F>) + Align(input.size() * sizeof(E));
+ }
+
+ static void SerializeElements(Array<E> input,
+ Buffer* buf,
+ Array_Data<F>* output,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params->element_is_nullable)
+ << "Primitive type should be non-nullable";
+ MOJO_DCHECK(!validate_params->element_validate_params)
+ << "Primitive type should not have array validate params";
+
+ if (input.size())
+ memcpy(output->storage(), &input.storage()[0], input.size() * sizeof(E));
+ }
+ static void DeserializeElements(Array_Data<F>* input, Array<E>* output) {
+ std::vector<E> result(input->size());
+ if (input->size())
+ memcpy(&result[0], input->storage(), input->size() * sizeof(E));
+ output->Swap(&result);
+ }
+};
+
+// Serializes and deserializes arrays of bools.
+template <>
+struct ArraySerializer<bool, bool, false> {
+ static size_t GetSerializedSize(const Array<bool>& input) {
+ return sizeof(Array_Data<bool>) + Align((input.size() + 7) / 8);
+ }
+
+ static void SerializeElements(Array<bool> input,
+ Buffer* buf,
+ Array_Data<bool>* output,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params->element_is_nullable)
+ << "Primitive type should be non-nullable";
+ MOJO_DCHECK(!validate_params->element_validate_params)
+ << "Primitive type should not have array validate params";
+
+ // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy?
+ for (size_t i = 0; i < input.size(); ++i)
+ output->at(i) = input[i];
+ }
+ static void DeserializeElements(Array_Data<bool>* input,
+ Array<bool>* output) {
+ Array<bool> result(input->size());
+ // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy?
+ for (size_t i = 0; i < input->size(); ++i)
+ result.at(i) = input->at(i);
+ output->Swap(&result);
+ }
+};
+
+// Serializes and deserializes arrays of handles.
+template <typename H>
+struct ArraySerializer<ScopedHandleBase<H>, H, false> {
+ static size_t GetSerializedSize(const Array<ScopedHandleBase<H>>& input) {
+ return sizeof(Array_Data<H>) + Align(input.size() * sizeof(H));
+ }
+
+ static void SerializeElements(Array<ScopedHandleBase<H>> input,
+ Buffer* buf,
+ Array_Data<H>* output,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params->element_validate_params)
+ << "Handle type should not have array validate params";
+
+ for (size_t i = 0; i < input.size(); ++i) {
+ output->at(i) = input[i].release(); // Transfer ownership of the handle.
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !validate_params->element_is_nullable && !output->at(i).is_valid(),
+ VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ MakeMessageWithArrayIndex(
+ "invalid handle in array expecting valid handles", input.size(),
+ i));
+ }
+ }
+ static void DeserializeElements(Array_Data<H>* input,
+ Array<ScopedHandleBase<H>>* output) {
+ Array<ScopedHandleBase<H>> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i)
+ result.at(i) = MakeScopedHandle(FetchAndReset(&input->at(i)));
+ output->Swap(&result);
+ }
+};
+
+// This template must only apply to pointer mojo entity (structs and arrays).
+// This is done by ensuring that WrapperTraits<S>::DataType is a pointer.
+template <typename S>
+struct ArraySerializer<
+ S,
+ typename EnableIf<IsPointer<typename WrapperTraits<S>::DataType>::value,
+ typename WrapperTraits<S>::DataType>::type,
+ false> {
+ typedef
+ typename RemovePointer<typename WrapperTraits<S>::DataType>::type S_Data;
+ static size_t GetSerializedSize(const Array<S>& input) {
+ size_t size = sizeof(Array_Data<S_Data*>) +
+ input.size() * sizeof(StructPointer<S_Data>);
+ for (size_t i = 0; i < input.size(); ++i)
+ size += GetSerializedSize_(input[i]);
+ return size;
+ }
+
+ static void SerializeElements(Array<S> input,
+ Buffer* buf,
+ Array_Data<S_Data*>* output,
+ const ArrayValidateParams* validate_params) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ S_Data* element;
+ SerializeCaller<S>::Run(input[i].Pass(), buf, &element,
+ validate_params->element_validate_params);
+ output->at(i) = element;
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !validate_params->element_is_nullable && !element,
+ VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ MakeMessageWithArrayIndex("null in array expecting valid pointers",
+ input.size(), i));
+ }
+ }
+ static void DeserializeElements(Array_Data<S_Data*>* input,
+ Array<S>* output) {
+ Array<S> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i) {
+ Deserialize_(input->at(i), &result[i]);
+ }
+ output->Swap(&result);
+ }
+
+ private:
+ template <typename T>
+ struct SerializeCaller {
+ static void Run(T input,
+ Buffer* buf,
+ typename WrapperTraits<T>::DataType* output,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(!validate_params)
+ << "Struct type should not have array validate params";
+
+ Serialize_(input.Pass(), buf, output);
+ }
+ };
+
+ template <typename T>
+ struct SerializeCaller<Array<T>> {
+ static void Run(Array<T> input,
+ Buffer* buf,
+ typename Array<T>::Data_** output,
+ const ArrayValidateParams* validate_params) {
+ SerializeArray_(input.Pass(), buf, output, validate_params);
+ }
+ };
+
+ template <typename T, typename U>
+ struct SerializeCaller<Map<T, U>> {
+ static void Run(Map<T, U> input,
+ Buffer* buf,
+ typename Map<T, U>::Data_** output,
+ const ArrayValidateParams* validate_params) {
+ SerializeMap_(input.Pass(), buf, output, validate_params);
+ }
+ };
+};
+
+// Handles serialization and deserialization of arrays of unions.
+template <typename U, typename U_Data>
+struct ArraySerializer<U, U_Data, true> {
+ static size_t GetSerializedSize(const Array<U>& input) {
+ size_t size = sizeof(Array_Data<U_Data>);
+ for (size_t i = 0; i < input.size(); ++i) {
+ // GetSerializedSize_ will account for both the data in the union and the
+ // space in the array used to hold the union.
+ size += GetSerializedSize_(input[i], false);
+ }
+ return size;
+ }
+
+ static void SerializeElements(Array<U> input,
+ Buffer* buf,
+ Array_Data<U_Data>* output,
+ const ArrayValidateParams* validate_params) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ U_Data* result = output->storage() + i;
+ SerializeUnion_(input[i].Pass(), buf, &result, true);
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !validate_params->element_is_nullable && output->at(i).is_null(),
+ VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ MakeMessageWithArrayIndex("null in array expecting valid unions",
+ input.size(), i));
+ }
+ }
+
+ static void DeserializeElements(Array_Data<U_Data>* input, Array<U>* output) {
+ Array<U> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i) {
+ Deserialize_(&input->at(i), &result[i]);
+ }
+ output->Swap(&result);
+ }
+};
+
+// Handles serialization and deserialization of arrays of strings.
+template <>
+struct ArraySerializer<String, String_Data*> {
+ static size_t GetSerializedSize(const Array<String>& input) {
+ size_t size =
+ sizeof(Array_Data<String_Data*>) + input.size() * sizeof(StringPointer);
+ for (size_t i = 0; i < input.size(); ++i)
+ size += GetSerializedSize_(input[i]);
+ return size;
+ }
+
+ static void SerializeElements(Array<String> input,
+ Buffer* buf,
+ Array_Data<String_Data*>* output,
+ const ArrayValidateParams* validate_params) {
+ MOJO_DCHECK(
+ validate_params->element_validate_params &&
+ !validate_params->element_validate_params->element_validate_params &&
+ !validate_params->element_validate_params->element_is_nullable &&
+ validate_params->element_validate_params->expected_num_elements == 0)
+ << "String type has unexpected array validate params";
+
+ for (size_t i = 0; i < input.size(); ++i) {
+ String_Data* element;
+ Serialize_(input[i], buf, &element);
+ output->at(i) = element;
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !validate_params->element_is_nullable && !element,
+ VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ MakeMessageWithArrayIndex("null in array expecting valid strings",
+ input.size(), i));
+ }
+ }
+ static void DeserializeElements(Array_Data<String_Data*>* input,
+ Array<String>* output) {
+ Array<String> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i)
+ Deserialize_(input->at(i), &result[i]);
+ output->Swap(&result);
+ }
+};
+
+} // namespace internal
+
+template <typename E>
+inline size_t GetSerializedSize_(const Array<E>& input) {
+ if (!input)
+ return 0;
+ typedef typename internal::WrapperTraits<E>::DataType F;
+ return internal::ArraySerializer<E, F>::GetSerializedSize(input);
+}
+
+template <typename E, typename F>
+inline void SerializeArray_(
+ Array<E> input,
+ internal::Buffer* buf,
+ internal::Array_Data<F>** output,
+ const internal::ArrayValidateParams* validate_params) {
+ if (input) {
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ validate_params->expected_num_elements != 0 &&
+ input.size() != validate_params->expected_num_elements,
+ internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ internal::MakeMessageWithExpectedArraySize(
+ "fixed-size array has wrong number of elements", input.size(),
+ validate_params->expected_num_elements));
+
+ internal::Array_Data<F>* result =
+ internal::Array_Data<F>::New(input.size(), buf);
+ if (result) {
+ internal::ArraySerializer<E, F>::SerializeElements(
+ internal::Forward(input), buf, result, validate_params);
+ }
+ *output = result;
+ } else {
+ *output = nullptr;
+ }
+}
+
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* input, Array<E>* output) {
+ if (input) {
+ internal::ArraySerializer<E, F>::DeserializeElements(input, output);
+ } else {
+ output->reset();
+ }
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h
new file mode 100644
index 0000000..436f580
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -0,0 +1,134 @@
+// Copyright 2013 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_BINDINGS_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+class Array_Data;
+
+#pragma pack(push, 1)
+
+struct StructHeader {
+ uint32_t num_bytes;
+ uint32_t version;
+};
+static_assert(sizeof(StructHeader) == 8, "Bad sizeof(StructHeader)");
+
+struct ArrayHeader {
+ uint32_t num_bytes;
+ uint32_t num_elements;
+};
+static_assert(sizeof(ArrayHeader) == 8, "Bad_sizeof(ArrayHeader)");
+
+template <typename T>
+union StructPointer {
+ uint64_t offset;
+ T* ptr;
+};
+static_assert(sizeof(StructPointer<char>) == 8, "Bad_sizeof(StructPointer)");
+
+template <typename T>
+union ArrayPointer {
+ uint64_t offset;
+ Array_Data<T>* ptr;
+};
+static_assert(sizeof(ArrayPointer<char>) == 8, "Bad_sizeof(ArrayPointer)");
+
+union StringPointer {
+ uint64_t offset;
+ Array_Data<char>* ptr;
+};
+static_assert(sizeof(StringPointer) == 8, "Bad_sizeof(StringPointer)");
+
+
+template <typename T>
+union UnionPointer {
+ uint64_t offset;
+ T* ptr;
+};
+static_assert(sizeof(UnionPointer<char>) == 8, "Bad_sizeof(UnionPointer)");
+
+struct Interface_Data {
+ MessagePipeHandle handle;
+ uint32_t version;
+};
+static_assert(sizeof(Interface_Data) == 8, "Bad_sizeof(Interface_Data)");
+
+struct AssociatedInterface_Data {
+ uint32_t interface_id;
+ uint32_t version;
+};
+static_assert(sizeof(AssociatedInterface_Data) == 8,
+ "Bad_sizeof(AssociatedInterface_Data)");
+
+using AssociatedInterfaceRequest_Data = uint32_t;
+
+#pragma pack(pop)
+
+template <typename T>
+void ResetIfNonNull(T* ptr) {
+ if (ptr)
+ *ptr = T();
+}
+
+template <typename T>
+T FetchAndReset(T* ptr) {
+ T temp = *ptr;
+ *ptr = T();
+ return temp;
+}
+
+template <typename H>
+struct IsHandle {
+ enum { value = IsBaseOf<Handle, H>::value };
+};
+
+template <typename T>
+struct IsUnionDataType {
+ template <typename U>
+ static YesType Test(const typename U::MojomUnionDataType*);
+
+ template <typename U>
+ static NoType Test(...);
+
+ static const bool value =
+ sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value;
+};
+
+template <typename T, bool move_only = IsMoveOnlyType<T>::value>
+struct WrapperTraits;
+
+template <typename T>
+struct WrapperTraits<T, false> {
+ typedef T DataType;
+};
+template <typename H>
+struct WrapperTraits<ScopedHandleBase<H>, true> {
+ typedef H DataType;
+};
+template <typename S>
+struct WrapperTraits<StructPtr<S>, true> {
+ typedef typename S::Data_* DataType;
+};
+template <typename S>
+struct WrapperTraits<InlinedStructPtr<S>, true> {
+ typedef typename S::Data_* DataType;
+};
+template <typename S>
+struct WrapperTraits<S, true> {
+ typedef typename S::Data_* DataType;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/bindings_serialization.cc b/mojo/public/cpp/bindings/lib/bindings_serialization.cc
new file mode 100644
index 0000000..791afe2
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_serialization.cc
@@ -0,0 +1,90 @@
+// Copyright 2013 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/lib/bindings_serialization.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+const size_t kAlignment = 8;
+
+template <typename T>
+T AlignImpl(T t) {
+ return t + (kAlignment - (t % kAlignment)) % kAlignment;
+}
+
+} // namespace
+
+size_t Align(size_t size) {
+ return AlignImpl(size);
+}
+
+char* AlignPointer(char* ptr) {
+ return reinterpret_cast<char*>(AlignImpl(reinterpret_cast<uintptr_t>(ptr)));
+}
+
+bool IsAligned(const void* ptr) {
+ return !(reinterpret_cast<uintptr_t>(ptr) % kAlignment);
+}
+
+void EncodePointer(const void* ptr, uint64_t* offset) {
+ if (!ptr) {
+ *offset = 0;
+ return;
+ }
+
+ const char* p_obj = reinterpret_cast<const char*>(ptr);
+ const char* p_slot = reinterpret_cast<const char*>(offset);
+ MOJO_DCHECK(p_obj > p_slot);
+
+ *offset = static_cast<uint64_t>(p_obj - p_slot);
+}
+
+const void* DecodePointerRaw(const uint64_t* offset) {
+ if (!*offset)
+ return nullptr;
+ return reinterpret_cast<const char*>(offset) + *offset;
+}
+
+void EncodeHandle(Handle* handle, std::vector<Handle>* handles) {
+ if (handle->is_valid()) {
+ handles->push_back(*handle);
+ handle->set_value(static_cast<MojoHandle>(handles->size() - 1));
+ } else {
+ handle->set_value(kEncodedInvalidHandleValue);
+ }
+}
+
+void EncodeHandle(Interface_Data* data, std::vector<Handle>* handles) {
+ EncodeHandle(&data->handle, handles);
+}
+
+void EncodeHandle(MojoHandle* handle, std::vector<Handle>* handles) {
+ EncodeHandle(reinterpret_cast<Handle*>(handle), handles);
+}
+
+void DecodeHandle(Handle* handle, std::vector<Handle>* handles) {
+ if (handle->value() == kEncodedInvalidHandleValue) {
+ *handle = Handle();
+ return;
+ }
+ MOJO_DCHECK(handle->value() < handles->size());
+ // Just leave holes in the vector so we don't screw up other indices.
+ *handle = FetchAndReset(&handles->at(handle->value()));
+}
+
+void DecodeHandle(Interface_Data* data, std::vector<Handle>* handles) {
+ DecodeHandle(&data->handle, handles);
+}
+
+void DecodeHandle(MojoHandle* handle, std::vector<Handle>* handles) {
+ DecodeHandle(reinterpret_cast<Handle*>(handle), handles);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/bindings_serialization.h b/mojo/public/cpp/bindings/lib/bindings_serialization.h
new file mode 100644
index 0000000..5e2f9a7
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_serialization.h
@@ -0,0 +1,91 @@
+// Copyright 2013 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_BINDINGS_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+// Please note that this is a different value than |mojo::kInvalidHandleValue|,
+// which is the "decoded" invalid handle.
+const MojoHandle kEncodedInvalidHandleValue = static_cast<MojoHandle>(-1);
+
+size_t Align(size_t size);
+char* AlignPointer(char* ptr);
+
+bool IsAligned(const void* ptr);
+
+// Pointers are encoded as relative offsets. The offsets are relative to the
+// address of where the offset value is stored, such that the pointer may be
+// recovered with the expression:
+//
+// ptr = reinterpret_cast<char*>(offset) + *offset
+//
+// A null pointer is encoded as an offset value of 0.
+//
+void EncodePointer(const void* ptr, uint64_t* offset);
+// Note: This function doesn't validate the encoded pointer value.
+const void* DecodePointerRaw(const uint64_t* offset);
+
+// Note: This function doesn't validate the encoded pointer value.
+template <typename T>
+inline void DecodePointer(const uint64_t* offset, T** ptr) {
+ *ptr = reinterpret_cast<T*>(const_cast<void*>(DecodePointerRaw(offset)));
+}
+
+// Handles are encoded as indices into a vector of handles. These functions
+// manipulate the value of |handle|, mapping it to and from an index.
+
+void EncodeHandle(Handle* handle, std::vector<Handle>* handles);
+void EncodeHandle(Interface_Data* data, std::vector<Handle>* handles);
+void EncodeHandle(MojoHandle* handle, std::vector<Handle>* handles);
+// Note: The following three functions don't validate the encoded handle value.
+void DecodeHandle(Handle* handle, std::vector<Handle>* handles);
+void DecodeHandle(Interface_Data* data, std::vector<Handle>* handles);
+void DecodeHandle(MojoHandle* handle, std::vector<Handle>* handles);
+
+// The following 2 functions are used to encode/decode all objects (structs and
+// arrays) in a consistent manner.
+
+template <typename T>
+inline void Encode(T* obj, std::vector<Handle>* handles) {
+ if (obj->ptr)
+ obj->ptr->EncodePointersAndHandles(handles);
+ EncodePointer(obj->ptr, &obj->offset);
+}
+
+// Note: This function doesn't validate the encoded pointer and handle values.
+template <typename T>
+inline void Decode(T* obj, std::vector<Handle>* handles) {
+ DecodePointer(&obj->offset, &obj->ptr);
+ if (obj->ptr)
+ obj->ptr->DecodePointersAndHandles(handles);
+}
+
+template <typename T>
+inline void InterfacePointerToData(InterfacePtr<T> input,
+ Interface_Data* output) {
+ InterfacePtrInfo<T> info = input.PassInterface();
+ output->handle = info.PassHandle().release();
+ output->version = info.version();
+}
+
+template <typename T>
+inline void InterfaceDataToPointer(Interface_Data* input,
+ InterfacePtr<T>* output) {
+ output->Bind(InterfacePtrInfo<T>(
+ MakeScopedHandle(FetchAndReset(&input->handle)), input->version));
+}
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.cc b/mojo/public/cpp/bindings/lib/bounds_checker.cc
new file mode 100644
index 0000000..5b96b2d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bounds_checker.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 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/lib/bounds_checker.h"
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+namespace internal {
+
+BoundsChecker::BoundsChecker(const void* data,
+ uint32_t data_num_bytes,
+ size_t num_handles)
+ : data_begin_(reinterpret_cast<uintptr_t>(data)),
+ data_end_(data_begin_ + data_num_bytes),
+ handle_begin_(0),
+ handle_end_(static_cast<uint32_t>(num_handles)) {
+ if (data_end_ < data_begin_) {
+ // The calculation of |data_end_| overflowed.
+ // It shouldn't happen but if it does, set the range to empty so
+ // IsValidRange() and ClaimMemory() always fail.
+ MOJO_DCHECK(false) << "Not reached";
+ data_end_ = data_begin_;
+ }
+ if (handle_end_ < num_handles) {
+ // Assigning |num_handles| to |handle_end_| overflowed.
+ // It shouldn't happen but if it does, set the handle index range to empty.
+ MOJO_DCHECK(false) << "Not reached";
+ handle_end_ = 0;
+ }
+}
+
+BoundsChecker::~BoundsChecker() {
+}
+
+bool BoundsChecker::ClaimMemory(const void* position, uint32_t num_bytes) {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+ uintptr_t end = begin + num_bytes;
+
+ if (!InternalIsValidRange(begin, end))
+ return false;
+
+ data_begin_ = end;
+ return true;
+}
+
+bool BoundsChecker::ClaimHandle(const Handle& encoded_handle) {
+ uint32_t index = encoded_handle.value();
+ if (index == kEncodedInvalidHandleValue)
+ return true;
+
+ if (index < handle_begin_ || index >= handle_end_)
+ return false;
+
+ // |index| + 1 shouldn't overflow, because |index| is not the max value of
+ // uint32_t (it is less than |handle_end_|).
+ handle_begin_ = index + 1;
+ return true;
+}
+
+bool BoundsChecker::IsValidRange(const void* position,
+ uint32_t num_bytes) const {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+ uintptr_t end = begin + num_bytes;
+
+ return InternalIsValidRange(begin, end);
+}
+
+bool BoundsChecker::InternalIsValidRange(uintptr_t begin, uintptr_t end) const {
+ return end > begin && begin >= data_begin_ && end <= data_end_;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.h b/mojo/public/cpp/bindings/lib/bounds_checker.h
new file mode 100644
index 0000000..f0520be
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bounds_checker.h
@@ -0,0 +1,63 @@
+// Copyright 2014 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_BOUNDS_CHECKER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class Handle;
+
+namespace internal {
+
+// BoundsChecker is used to validate object sizes, pointers and handle indices
+// for payload of incoming messages.
+class BoundsChecker {
+ public:
+ // [data, data + data_num_bytes) specifies the initial valid memory range.
+ // [0, num_handles) specifies the initial valid range of handle indices.
+ BoundsChecker(const void* data, uint32_t data_num_bytes, size_t num_handles);
+
+ ~BoundsChecker();
+
+ // Claims the specified memory range.
+ // The method succeeds if the range is valid to claim. (Please see
+ // the comments for IsValidRange().)
+ // On success, the valid memory range is shrinked to begin right after the end
+ // of the claimed range.
+ bool ClaimMemory(const void* position, uint32_t num_bytes);
+
+ // Claims the specified encoded handle (which is basically a handle index).
+ // The method succeeds if:
+ // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|.
+ // - the handle is contained inside the valid range of handle indices. In this
+ // case, the valid range is shinked to begin right after the claimed handle.
+ bool ClaimHandle(const Handle& encoded_handle);
+
+ // Returns true if the specified range is not empty, and the range is
+ // contained inside the valid memory range.
+ bool IsValidRange(const void* position, uint32_t num_bytes) const;
+
+ private:
+ bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const;
+
+ // [data_begin_, data_end_) is the valid memory range.
+ uintptr_t data_begin_;
+ uintptr_t data_end_;
+
+ // [handle_begin_, handle_end_) is the valid handle index range.
+ uint32_t handle_begin_;
+ uint32_t handle_end_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChecker);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
new file mode 100644
index 0000000..c3b570e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -0,0 +1,24 @@
+// Copyright 2013 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_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
+
+#include <stddef.h>
+
+namespace mojo {
+namespace internal {
+
+// Buffer provides a way to allocate memory. Allocations are 8-byte aligned and
+// zero-initialized. Allocations remain valid for the lifetime of the Buffer.
+class Buffer {
+ public:
+ virtual ~Buffer() {}
+ virtual void* Allocate(size_t num_bytes) = 0;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/callback_internal.h b/mojo/public/cpp/bindings/lib/callback_internal.h
new file mode 100644
index 0000000..9df5b40a7
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/callback_internal.h
@@ -0,0 +1,53 @@
+// Copyright 2014 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_CALLBACK_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+class String;
+
+namespace internal {
+
+template <typename T>
+struct Callback_ParamTraits {
+ typedef T ForwardType;
+};
+
+template <>
+struct Callback_ParamTraits<String> {
+ typedef const String& ForwardType;
+};
+
+template <typename T, typename... Args>
+struct HasCompatibleCallOperator {
+ // This template's second parameter is the signature of the operator()
+ // overload we want to try to detect:
+ // void operator()(Args...) const;
+ template <typename U,
+ void (U::*)(
+ typename internal::Callback_ParamTraits<Args>::ForwardType...)
+ const>
+ struct TestType {};
+
+ // This matches type U if it has a call operator with the
+ // expected signature.
+ template <typename U>
+ static YesType Test(TestType<U, &U::operator()>*);
+
+ // This matches anything else.
+ template <typename U>
+ static NoType Test(...);
+
+ // HasCompatibleCallOperator<T, Args...>::value will be true if T has a
+ // compatible call operator.
+ enum { value = (sizeof(Test<T>(nullptr)) == sizeof(YesType)) };
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
new file mode 100644
index 0000000..5bd94eb
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -0,0 +1,259 @@
+// Copyright 2013 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/lib/connector.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+Connector::Connector(ScopedMessagePipeHandle message_pipe,
+ const MojoAsyncWaiter* waiter)
+ : waiter_(waiter),
+ message_pipe_(message_pipe.Pass()),
+ incoming_receiver_(nullptr),
+ async_wait_id_(0),
+ error_(false),
+ drop_writes_(false),
+ enforce_errors_from_incoming_receiver_(true),
+ paused_(false),
+ destroyed_flag_(nullptr) {
+ // Even though we don't have an incoming receiver, we still want to monitor
+ // the message pipe to know if is closed or encounters an error.
+ WaitToReadMore();
+}
+
+Connector::~Connector() {
+ if (destroyed_flag_)
+ *destroyed_flag_ = true;
+
+ CancelWait();
+}
+
+void Connector::CloseMessagePipe() {
+ CancelWait();
+ Close(message_pipe_.Pass());
+}
+
+ScopedMessagePipeHandle Connector::PassMessagePipe() {
+ CancelWait();
+ return message_pipe_.Pass();
+}
+
+void Connector::RaiseError() {
+ HandleError(true, true);
+}
+
+bool Connector::WaitForIncomingMessage(MojoDeadline deadline) {
+ if (error_)
+ return false;
+
+ ResumeIncomingMethodCallProcessing();
+
+ MojoResult rv =
+ Wait(message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, deadline, nullptr);
+ if (rv == MOJO_RESULT_SHOULD_WAIT || rv == MOJO_RESULT_DEADLINE_EXCEEDED)
+ return false;
+ if (rv != MOJO_RESULT_OK) {
+ // Users that call WaitForIncomingMessage() should expect their code to be
+ // re-entered, so we call the error handler synchronously.
+ HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
+ return false;
+ }
+ mojo_ignore_result(ReadSingleMessage(&rv));
+ return (rv == MOJO_RESULT_OK);
+}
+
+void Connector::PauseIncomingMethodCallProcessing() {
+ if (paused_)
+ return;
+
+ paused_ = true;
+ CancelWait();
+}
+
+void Connector::ResumeIncomingMethodCallProcessing() {
+ if (!paused_)
+ return;
+
+ paused_ = false;
+ WaitToReadMore();
+}
+
+bool Connector::Accept(Message* message) {
+ if (error_)
+ return false;
+
+ MOJO_CHECK(message_pipe_.is_valid());
+ if (drop_writes_)
+ return true;
+
+ MojoResult rv =
+ WriteMessageRaw(message_pipe_.get(),
+ message->data(),
+ message->data_num_bytes(),
+ message->mutable_handles()->empty()
+ ? nullptr
+ : reinterpret_cast<const MojoHandle*>(
+ &message->mutable_handles()->front()),
+ static_cast<uint32_t>(message->mutable_handles()->size()),
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+ switch (rv) {
+ case MOJO_RESULT_OK:
+ // The handles were successfully transferred, so we don't need the message
+ // to track their lifetime any longer.
+ message->mutable_handles()->clear();
+ break;
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ // There's no point in continuing to write to this pipe since the other
+ // end is gone. Avoid writing any future messages. Hide write failures
+ // from the caller since we'd like them to continue consuming any backlog
+ // of incoming messages before regarding the message pipe as closed.
+ drop_writes_ = true;
+ break;
+ case MOJO_RESULT_BUSY:
+ // We'd get a "busy" result if one of the message's handles is:
+ // - |message_pipe_|'s own handle;
+ // - simultaneously being used on another thread; or
+ // - in a "busy" state that prohibits it from being transferred (e.g.,
+ // a data pipe handle in the middle of a two-phase read/write,
+ // regardless of which thread that two-phase read/write is happening
+ // on).
+ // TODO(vtl): I wonder if this should be a |MOJO_DCHECK()|. (But, until
+ // crbug.com/389666, etc. are resolved, this will make tests fail quickly
+ // rather than hanging.)
+ MOJO_CHECK(false) << "Race condition or other bug detected";
+ return false;
+ default:
+ // This particular write was rejected, presumably because of bad input.
+ // The pipe is not necessarily in a bad state.
+ return false;
+ }
+ return true;
+}
+
+// static
+void Connector::CallOnHandleReady(void* closure, MojoResult result) {
+ Connector* self = static_cast<Connector*>(closure);
+ self->OnHandleReady(result);
+}
+
+void Connector::OnHandleReady(MojoResult result) {
+ MOJO_CHECK(async_wait_id_ != 0);
+ async_wait_id_ = 0;
+ if (result != MOJO_RESULT_OK) {
+ HandleError(result != MOJO_RESULT_FAILED_PRECONDITION, false);
+ return;
+ }
+ ReadAllAvailableMessages();
+ // At this point, this object might have been deleted. Return.
+}
+
+void Connector::WaitToReadMore() {
+ MOJO_CHECK(!async_wait_id_);
+ async_wait_id_ = waiter_->AsyncWait(message_pipe_.get().value(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ &Connector::CallOnHandleReady,
+ this);
+}
+
+bool Connector::ReadSingleMessage(MojoResult* read_result) {
+ bool receiver_result = false;
+
+ // Detect if |this| was destroyed during message dispatch. Allow for the
+ // possibility of re-entering ReadMore() through message dispatch.
+ bool was_destroyed_during_dispatch = false;
+ bool* previous_destroyed_flag = destroyed_flag_;
+ destroyed_flag_ = &was_destroyed_during_dispatch;
+
+ MojoResult rv = ReadAndDispatchMessage(
+ message_pipe_.get(), incoming_receiver_, &receiver_result);
+ if (read_result)
+ *read_result = rv;
+
+ if (was_destroyed_during_dispatch) {
+ if (previous_destroyed_flag)
+ *previous_destroyed_flag = true; // Propagate flag.
+ return false;
+ }
+ destroyed_flag_ = previous_destroyed_flag;
+
+ if (rv == MOJO_RESULT_SHOULD_WAIT)
+ return true;
+
+ if (rv != MOJO_RESULT_OK) {
+ HandleError(rv != MOJO_RESULT_FAILED_PRECONDITION, false);
+ return false;
+ }
+
+ if (enforce_errors_from_incoming_receiver_ && !receiver_result) {
+ HandleError(true, false);
+ return false;
+ }
+ return true;
+}
+
+void Connector::ReadAllAvailableMessages() {
+ while (!error_) {
+ MojoResult rv;
+
+ // Return immediately if |this| was destroyed. Do not touch any members!
+ if (!ReadSingleMessage(&rv))
+ return;
+
+ if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ WaitToReadMore();
+ break;
+ }
+ }
+}
+
+void Connector::CancelWait() {
+ if (!async_wait_id_)
+ return;
+
+ waiter_->CancelWait(async_wait_id_);
+ async_wait_id_ = 0;
+}
+
+void Connector::HandleError(bool force_pipe_reset, bool force_async_handler) {
+ if (error_ || !message_pipe_.is_valid())
+ return;
+
+ if (!force_pipe_reset && force_async_handler)
+ force_pipe_reset = true;
+
+ if (paused_) {
+ // If the user has paused receiving messages, we shouldn't call the error
+ // handler right away. We need to wait until the user starts receiving
+ // messages again.
+ force_async_handler = true;
+ }
+
+ if (force_pipe_reset) {
+ CloseMessagePipe();
+ MessagePipe dummy_pipe;
+ message_pipe_ = dummy_pipe.handle0.Pass();
+ } else {
+ CancelWait();
+ }
+
+ if (force_async_handler) {
+ // |dummy_pipe.handle1| has been destructed. Reading the pipe will
+ // eventually cause a read error on |message_pipe_| and set error state.
+ if (!paused_)
+ WaitToReadMore();
+ } else {
+ error_ = true;
+ connection_error_handler_.Run();
+ }
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/connector.h b/mojo/public/cpp/bindings/lib/connector.h
new file mode 100644
index 0000000..185a984
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.h
@@ -0,0 +1,142 @@
+// Copyright 2013 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_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+class ErrorHandler;
+
+namespace internal {
+
+// The Connector class is responsible for performing read/write operations on a
+// MessagePipe. It writes messages it receives through the MessageReceiver
+// interface that it subclasses, and it forwards messages it reads through the
+// MessageReceiver interface assigned as its incoming receiver.
+//
+// NOTE: MessagePipe I/O is non-blocking.
+//
+class Connector : public MessageReceiver {
+ public:
+ // The Connector takes ownership of |message_pipe|.
+ explicit Connector(
+ ScopedMessagePipeHandle message_pipe,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+ ~Connector() override;
+
+ // Sets the receiver to handle messages read from the message pipe. The
+ // Connector will read messages from the pipe regardless of whether or not an
+ // incoming receiver has been set.
+ void set_incoming_receiver(MessageReceiver* receiver) {
+ incoming_receiver_ = receiver;
+ }
+
+ // Errors from incoming receivers will force the connector into an error
+ // state, where no more messages will be processed. This method is used
+ // during testing to prevent that from happening.
+ void set_enforce_errors_from_incoming_receiver(bool enforce) {
+ enforce_errors_from_incoming_receiver_ = enforce;
+ }
+
+ // Sets the error handler to receive notifications when an error is
+ // encountered while reading from the pipe or waiting to read from the pipe.
+ void set_connection_error_handler(const Closure& error_handler) {
+ connection_error_handler_ = error_handler;
+ }
+
+ // Returns true if an error was encountered while reading from the pipe or
+ // waiting to read from the pipe.
+ bool encountered_error() const { return error_; }
+
+ // Closes the pipe. The connector is put into a quiescent state.
+ //
+ // Please note that this method shouldn't be called unless it results from an
+ // explicit request of the user of bindings (e.g., the user sets an
+ // InterfacePtr to null or closes a Binding).
+ void CloseMessagePipe();
+
+ // Releases the pipe. Connector is put into a quiescent state.
+ ScopedMessagePipeHandle PassMessagePipe();
+
+ // Enters the error state. The upper layer may do this for unrecoverable
+ // issues such as invalid messages are received. If a connection error handler
+ // has been set, it will be called asynchronously.
+ //
+ // It is a no-op if the connector is already in the error state or there isn't
+ // a bound message pipe. Otherwise, it closes the message pipe, which notifies
+ // the other end and also prevents potential danger (say, the caller raises
+ // an error because it believes the other end is malicious). In order to
+ // appear to the user that the connector still binds to a message pipe, it
+ // creates a new message pipe, closes one end and binds to the other.
+ void RaiseError();
+
+ // Is the connector bound to a MessagePipe handle?
+ bool is_valid() const { return message_pipe_.is_valid(); }
+
+ // Waits for the next message on the pipe, blocking until one arrives,
+ // |deadline| elapses, or an error happens. Returns |true| if a message has
+ // been delivered, |false| otherwise.
+ bool WaitForIncomingMessage(MojoDeadline deadline);
+
+ // See Binding for details of pause/resume.
+ void PauseIncomingMethodCallProcessing();
+ void ResumeIncomingMethodCallProcessing();
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override;
+
+ MessagePipeHandle handle() const { return message_pipe_.get(); }
+
+ private:
+ static void CallOnHandleReady(void* closure, MojoResult result);
+ void OnHandleReady(MojoResult result);
+
+ void WaitToReadMore();
+
+ // Returns false if |this| was destroyed during message dispatch.
+ MOJO_WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result);
+
+ // |this| can be destroyed during message dispatch.
+ void ReadAllAvailableMessages();
+
+ // If |force_pipe_reset| is true, this method replaces the existing
+ // |message_pipe_| with a dummy message pipe handle (whose peer is closed).
+ // If |force_async_handler| is true, |connection_error_handler_| is called
+ // asynchronously.
+ void HandleError(bool force_pipe_reset, bool force_async_handler);
+
+ // Cancels any calls made to |waiter_|.
+ void CancelWait();
+
+ Closure connection_error_handler_;
+ const MojoAsyncWaiter* waiter_;
+
+ ScopedMessagePipeHandle message_pipe_;
+ MessageReceiver* incoming_receiver_;
+
+ MojoAsyncWaitID async_wait_id_;
+ bool error_;
+ bool drop_writes_;
+ bool enforce_errors_from_incoming_receiver_;
+
+ bool paused_;
+
+ // If non-null, this will be set to true when the Connector is destroyed. We
+ // use this flag to allow for the Connector to be destroyed as a side-effect
+ // of dispatching an incoming message.
+ bool* destroyed_flag_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Connector);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc
new file mode 100644
index 0000000..5113bb0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc
@@ -0,0 +1,81 @@
+// Copyright 2015 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/lib/control_message_handler.h"
+
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+// static
+bool ControlMessageHandler::IsControlMessage(const Message* message) {
+ return message->header()->name == kRunMessageId ||
+ message->header()->name == kRunOrClosePipeMessageId;
+}
+
+ControlMessageHandler::ControlMessageHandler(uint32_t interface_version)
+ : interface_version_(interface_version) {
+}
+
+ControlMessageHandler::~ControlMessageHandler() {
+}
+
+bool ControlMessageHandler::Accept(Message* message) {
+ if (message->header()->name == kRunOrClosePipeMessageId)
+ return RunOrClosePipe(message);
+
+ MOJO_NOTREACHED();
+ return false;
+}
+
+bool ControlMessageHandler::AcceptWithResponder(
+ Message* message,
+ MessageReceiverWithStatus* responder) {
+ if (message->header()->name == kRunMessageId)
+ return Run(message, responder);
+
+ MOJO_NOTREACHED();
+ return false;
+}
+
+bool ControlMessageHandler::Run(Message* message,
+ MessageReceiverWithStatus* responder) {
+ RunResponseMessageParamsPtr response_params_ptr(
+ RunResponseMessageParams::New());
+ response_params_ptr->reserved0 = 16u;
+ response_params_ptr->reserved1 = 0u;
+ response_params_ptr->query_version_result = QueryVersionResult::New();
+ response_params_ptr->query_version_result->version = interface_version_;
+
+ size_t size = GetSerializedSize_(response_params_ptr);
+ ResponseMessageBuilder builder(kRunMessageId, size, message->request_id());
+
+ RunResponseMessageParams_Data* response_params = nullptr;
+ Serialize_(response_params_ptr.Pass(), builder.buffer(), &response_params);
+ response_params->EncodePointersAndHandles(
+ builder.message()->mutable_handles());
+ bool ok = responder->Accept(builder.message());
+ MOJO_ALLOW_UNUSED_LOCAL(ok);
+ delete responder;
+
+ return true;
+}
+
+bool ControlMessageHandler::RunOrClosePipe(Message* message) {
+ RunOrClosePipeMessageParams_Data* params =
+ reinterpret_cast<RunOrClosePipeMessageParams_Data*>(
+ message->mutable_payload());
+ params->DecodePointersAndHandles(message->mutable_handles());
+
+ RunOrClosePipeMessageParamsPtr params_ptr;
+ Deserialize_(params, &params_ptr);
+
+ return interface_version_ >= params_ptr->require_version->version;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h
new file mode 100644
index 0000000..4a2fed5
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.h
@@ -0,0 +1,42 @@
+// Copyright 2015 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_CONTROL_MESSAGE_HANDLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Handlers for request messages defined in interface_control_messages.mojom.
+class ControlMessageHandler : public MessageReceiverWithResponderStatus {
+ public:
+ static bool IsControlMessage(const Message* message);
+
+ explicit ControlMessageHandler(uint32_t interface_version);
+ ~ControlMessageHandler() override;
+
+ // Call the following methods only if IsControlMessage() returned true.
+ bool Accept(Message* message) override;
+ // Takes ownership of |responder|.
+ bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder) override;
+
+ private:
+ bool Run(Message* message, MessageReceiverWithStatus* responder);
+ bool RunOrClosePipe(Message* message);
+
+ uint32_t interface_version_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ControlMessageHandler);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_HANDLER_H_
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
new file mode 100644
index 0000000..ad729c5
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
@@ -0,0 +1,100 @@
+// Copyright 2015 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/lib/control_message_proxy.h"
+
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+using RunCallback = Callback<void(QueryVersionResultPtr)>;
+
+class RunResponseForwardToCallback : public MessageReceiver {
+ public:
+ RunResponseForwardToCallback(const RunCallback& callback)
+ : callback_(callback) {}
+ bool Accept(Message* message) override;
+
+ private:
+ RunCallback callback_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RunResponseForwardToCallback);
+};
+
+bool RunResponseForwardToCallback::Accept(Message* message) {
+ RunResponseMessageParams_Data* params =
+ reinterpret_cast<RunResponseMessageParams_Data*>(
+ message->mutable_payload());
+ params->DecodePointersAndHandles(message->mutable_handles());
+
+ RunResponseMessageParamsPtr params_ptr;
+ Deserialize_(params, &params_ptr);
+
+ callback_.Run(params_ptr->query_version_result.Pass());
+ return true;
+}
+
+void SendRunMessage(MessageReceiverWithResponder* receiver,
+ QueryVersionPtr query_version,
+ const RunCallback& callback) {
+ RunMessageParamsPtr params_ptr(RunMessageParams::New());
+ params_ptr->reserved0 = 16u;
+ params_ptr->reserved1 = 0u;
+ params_ptr->query_version = query_version.Pass();
+
+ size_t size = GetSerializedSize_(params_ptr);
+ RequestMessageBuilder builder(kRunMessageId, size);
+
+ RunMessageParams_Data* params = nullptr;
+ Serialize_(params_ptr.Pass(), builder.buffer(), &params);
+ params->EncodePointersAndHandles(builder.message()->mutable_handles());
+ MessageReceiver* responder = new RunResponseForwardToCallback(callback);
+ if (!receiver->AcceptWithResponder(builder.message(), responder))
+ delete responder;
+}
+
+void SendRunOrClosePipeMessage(MessageReceiverWithResponder* receiver,
+ RequireVersionPtr require_version) {
+ RunOrClosePipeMessageParamsPtr params_ptr(RunOrClosePipeMessageParams::New());
+ params_ptr->reserved0 = 16u;
+ params_ptr->reserved1 = 0u;
+ params_ptr->require_version = require_version.Pass();
+
+ size_t size = GetSerializedSize_(params_ptr);
+ MessageBuilder builder(kRunOrClosePipeMessageId, size);
+
+ RunOrClosePipeMessageParams_Data* params = nullptr;
+ Serialize_(params_ptr.Pass(), builder.buffer(), &params);
+ params->EncodePointersAndHandles(builder.message()->mutable_handles());
+ bool ok = receiver->Accept(builder.message());
+ MOJO_ALLOW_UNUSED_LOCAL(ok);
+}
+
+} // namespace
+
+ControlMessageProxy::ControlMessageProxy(MessageReceiverWithResponder* receiver)
+ : receiver_(receiver) {
+}
+
+void ControlMessageProxy::QueryVersion(
+ const Callback<void(uint32_t)>& callback) {
+ auto run_callback = [callback](QueryVersionResultPtr query_version_result) {
+ callback.Run(query_version_result->version);
+ };
+ SendRunMessage(receiver_, QueryVersion::New(), run_callback);
+}
+
+void ControlMessageProxy::RequireVersion(uint32_t version) {
+ RequireVersionPtr require_version(RequireVersion::New());
+ require_version->version = version;
+ SendRunOrClosePipeMessage(receiver_, require_version.Pass());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h
new file mode 100644
index 0000000..5b0f018
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h
@@ -0,0 +1,38 @@
+// Copyright 2015 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_CONTROL_MESSAGE_PROXY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class MessageReceiverWithResponder;
+
+namespace internal {
+
+// Proxy for request messages defined in interface_control_messages.mojom.
+class ControlMessageProxy {
+ public:
+ // Doesn't take ownership of |receiver|. It must outlive this object.
+ explicit ControlMessageProxy(MessageReceiverWithResponder* receiver);
+
+ void QueryVersion(const Callback<void(uint32_t)>& callback);
+ void RequireVersion(uint32_t version);
+
+ protected:
+ // Not owned.
+ MessageReceiverWithResponder* receiver_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONTROL_MESSAGE_PROXY_H_
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc
new file mode 100644
index 0000000..d32eb78
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/filter_chain.cc
@@ -0,0 +1,49 @@
+// Copyright 2014 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/lib/filter_chain.h"
+
+#include <algorithm>
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) {
+}
+
+FilterChain::FilterChain(FilterChain&& other) : sink_(other.sink_) {
+ other.sink_ = nullptr;
+ filters_.swap(other.filters_);
+}
+
+FilterChain& FilterChain::operator=(FilterChain&& other) {
+ std::swap(sink_, other.sink_);
+ filters_.swap(other.filters_);
+ return *this;
+}
+
+FilterChain::~FilterChain() {
+ for (std::vector<MessageFilter*>::iterator iter = filters_.begin();
+ iter != filters_.end();
+ ++iter) {
+ delete *iter;
+ }
+}
+
+void FilterChain::SetSink(MessageReceiver* sink) {
+ MOJO_DCHECK(!sink_);
+ sink_ = sink;
+ if (!filters_.empty())
+ filters_.back()->set_sink(sink);
+}
+
+MessageReceiver* FilterChain::GetHead() {
+ MOJO_DCHECK(sink_);
+ return filters_.empty() ? sink_ : filters_.front();
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.h b/mojo/public/cpp/bindings/lib/filter_chain.h
new file mode 100644
index 0000000..bd7f9f5
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/filter_chain.h
@@ -0,0 +1,64 @@
+// Copyright 2014 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_FILTER_CHAIN_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+class FilterChain {
+ MOJO_MOVE_ONLY_TYPE(FilterChain)
+
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit FilterChain(MessageReceiver* sink = nullptr);
+
+ FilterChain(FilterChain&& other);
+ FilterChain& operator=(FilterChain&& other);
+ ~FilterChain();
+
+ template <typename FilterType>
+ inline void Append();
+
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ void SetSink(MessageReceiver* sink);
+
+ // Returns a receiver to accept messages. Messages flow through all filters in
+ // the same order as they were appended to the chain. If all filters allow a
+ // message to pass, it will be forwarded to |sink_|.
+ // The returned value is invalidated when this object goes away.
+ MessageReceiver* GetHead();
+
+ private:
+ // Owned by this object.
+ std::vector<MessageFilter*> filters_;
+
+ MessageReceiver* sink_;
+};
+
+template <typename FilterType>
+inline void FilterChain::Append() {
+ FilterType* filter = new FilterType(sink_);
+ if (!filters_.empty())
+ filters_.back()->set_sink(filter);
+ filters_.push_back(filter);
+}
+
+template <>
+inline void FilterChain::Append<PassThroughFilter>() {
+}
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
new file mode 100644
index 0000000..c81fc6e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 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/lib/fixed_buffer.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+FixedBuffer::FixedBuffer() : ptr_(nullptr), cursor_(0), size_(0) {}
+
+void FixedBuffer::Initialize(void* memory, size_t size) {
+ MOJO_DCHECK(size == internal::Align(size));
+
+ ptr_ = static_cast<char*>(memory);
+ cursor_ = 0;
+ size_ = size;
+}
+
+void* FixedBuffer::Allocate(size_t delta) {
+ delta = internal::Align(delta);
+
+ if (delta == 0 || delta > size_ - cursor_) {
+ MOJO_DCHECK(false) << "Not reached";
+ return nullptr;
+ }
+
+ char* result = ptr_ + cursor_;
+ cursor_ += delta;
+
+ return result;
+}
+
+FixedBufferForTesting::FixedBufferForTesting(size_t size) {
+ size_ = internal::Align(size);
+ // Use calloc here to ensure all message memory is zero'd out.
+ ptr_ = static_cast<char*>(calloc(size_, 1));
+}
+
+FixedBufferForTesting::~FixedBufferForTesting() {
+ free(ptr_);
+}
+
+void* FixedBufferForTesting::Leak() {
+ char* ptr = ptr_;
+ ptr_ = nullptr;
+ cursor_ = 0;
+ size_ = 0;
+ return ptr;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
new file mode 100644
index 0000000..83eaf97
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -0,0 +1,78 @@
+// Copyright 2014 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_FIXED_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// FixedBuffer provides a simple way to allocate objects within a fixed chunk
+// of memory. Objects are allocated by calling the |Allocate| method, which
+// extends the buffer accordingly. Objects allocated in this way are not freed
+// explicitly. Instead, they remain valid so long as the FixedBuffer remains
+// valid. The Leak method may be used to steal the underlying memory from the
+// FixedBuffer.
+//
+// Typical usage:
+//
+// {
+// FixedBuffer buf(8 + 8);
+//
+// int* a = static_cast<int*>(buf->Allocate(sizeof(int)));
+// *a = 2;
+//
+// double* b = static_cast<double*>(buf->Allocate(sizeof(double)));
+// *b = 3.14f;
+//
+// void* data = buf.Leak();
+// Process(data);
+//
+// free(data);
+// }
+
+class FixedBuffer : public Buffer {
+ public:
+ FixedBuffer();
+
+ // |size| should be aligned using internal::Align.
+ void Initialize(void* memory, size_t size);
+
+ size_t size() const { return size_; }
+
+ // Grows the buffer by |num_bytes| and returns a pointer to the start of the
+ // addition. The resulting address is 8-byte aligned, and the content of the
+ // memory is zero-filled.
+ void* Allocate(size_t num_bytes) override;
+
+ protected:
+ char* ptr_;
+ size_t cursor_;
+ size_t size_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBuffer);
+};
+
+class FixedBufferForTesting : public FixedBuffer {
+ public:
+ explicit FixedBufferForTesting(size_t size);
+ ~FixedBufferForTesting() override;
+
+ // Returns the internal memory owned by the Buffer to the caller. The Buffer
+ // relinquishes its pointer, effectively resetting the state of the Buffer
+ // and leaving the caller responsible for freeing the returned memory address
+ // when no longer needed.
+ void* Leak();
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBufferForTesting);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_internal.h b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
new file mode 100644
index 0000000..1c2cad8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
@@ -0,0 +1,174 @@
+// Copyright 2014 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_INTERFACE_PTR_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
+
+#include <algorithm> // For |std::swap()|.
+
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+struct MojoAsyncWaiter;
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class InterfacePtrState {
+ public:
+ InterfacePtrState()
+ : proxy_(nullptr), router_(nullptr), waiter_(nullptr), version_(0u) {}
+
+ ~InterfacePtrState() {
+ // Destruction order matters here. We delete |proxy_| first, even though
+ // |router_| may have a reference to it, so that destructors for any request
+ // callbacks still pending can interact with the InterfacePtr.
+ delete proxy_;
+ delete router_;
+ }
+
+ Interface* instance() {
+ ConfigureProxyIfNecessary();
+
+ // This will be null if the object is not bound.
+ return proxy_;
+ }
+
+ uint32_t version() const { return version_; }
+
+ void QueryVersion(const Callback<void(uint32_t)>& callback) {
+ ConfigureProxyIfNecessary();
+
+ // It is safe to capture |this| because the callback won't be run after this
+ // object goes away.
+ auto callback_wrapper = [this, callback](uint32_t version) {
+ this->version_ = version;
+ callback.Run(version);
+ };
+
+ // Do a static cast in case the interface contains methods with the same
+ // name.
+ static_cast<ControlMessageProxy*>(proxy_)->QueryVersion(callback_wrapper);
+ }
+
+ void RequireVersion(uint32_t version) {
+ ConfigureProxyIfNecessary();
+
+ if (version <= version_)
+ return;
+
+ version_ = version;
+ // Do a static cast in case the interface contains methods with the same
+ // name.
+ static_cast<ControlMessageProxy*>(proxy_)->RequireVersion(version);
+ }
+
+ void Swap(InterfacePtrState* other) {
+ using std::swap;
+ swap(other->proxy_, proxy_);
+ swap(other->router_, router_);
+ handle_.swap(other->handle_);
+ swap(other->waiter_, waiter_);
+ swap(other->version_, version_);
+ }
+
+ void Bind(InterfacePtrInfo<Interface> info, const MojoAsyncWaiter* waiter) {
+ MOJO_DCHECK(!proxy_);
+ MOJO_DCHECK(!router_);
+ MOJO_DCHECK(!handle_.is_valid());
+ MOJO_DCHECK(!waiter_);
+ MOJO_DCHECK(version_ == 0u);
+ MOJO_DCHECK(info.is_valid());
+
+ handle_ = info.PassHandle();
+ waiter_ = waiter;
+ version_ = info.version();
+ }
+
+ bool WaitForIncomingResponse() {
+ ConfigureProxyIfNecessary();
+
+ MOJO_DCHECK(router_);
+ return router_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+ }
+
+ // After this method is called, the object is in an invalid state and
+ // shouldn't be reused.
+ InterfacePtrInfo<Interface> PassInterface() {
+ return InterfacePtrInfo<Interface>(
+ router_ ? router_->PassMessagePipe() : handle_.Pass(), version_);
+ }
+
+ bool is_bound() const { return handle_.is_valid() || router_; }
+
+ bool encountered_error() const {
+ return router_ ? router_->encountered_error() : false;
+ }
+
+ void set_connection_error_handler(const Closure& error_handler) {
+ ConfigureProxyIfNecessary();
+
+ MOJO_DCHECK(router_);
+ router_->set_connection_error_handler(error_handler);
+ }
+
+ // Returns true if bound and awaiting a response to a message.
+ bool has_pending_callbacks() const {
+ return router_ && router_->has_pending_responders();
+ }
+
+ Router* router_for_testing() {
+ ConfigureProxyIfNecessary();
+ return router_;
+ }
+
+ private:
+ using Proxy = typename Interface::Proxy_;
+
+ void ConfigureProxyIfNecessary() {
+ // The proxy has been configured.
+ if (proxy_) {
+ MOJO_DCHECK(router_);
+ return;
+ }
+ // The object hasn't been bound.
+ if (!waiter_) {
+ MOJO_DCHECK(!handle_.is_valid());
+ return;
+ }
+
+ FilterChain filters;
+ filters.Append<MessageHeaderValidator>();
+ filters.Append<typename Interface::ResponseValidator_>();
+
+ router_ = new Router(handle_.Pass(), filters.Pass(), waiter_);
+ waiter_ = nullptr;
+
+ proxy_ = new Proxy(router_);
+ }
+
+ Proxy* proxy_;
+ Router* router_;
+
+ // |proxy_| and |router_| are not initialized until read/write with the
+ // message pipe handle is needed. |handle_| and |waiter_| are valid between
+ // the Bind() call and the initialization of |proxy_| and |router_|.
+ ScopedMessagePipeHandle handle_;
+ const MojoAsyncWaiter* waiter_;
+
+ uint32_t version_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfacePtrState);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/map_data_internal.h b/mojo/public/cpp/bindings/lib/map_data_internal.h
new file mode 100644
index 0000000..8315dbc
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/map_data_internal.h
@@ -0,0 +1,141 @@
+// Copyright 2014 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_MAP_DATA_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+namespace mojo {
+namespace internal {
+
+inline const ArrayValidateParams* GetMapKeyValidateParamsDefault() {
+ // The memory allocated here never gets released to not cause an exit time
+ // destructor.
+ static const ArrayValidateParams* validate_params =
+ new ArrayValidateParams(0, false, nullptr);
+ return validate_params;
+}
+
+inline const ArrayValidateParams* GetMapKeyValidateParamsForStrings() {
+ // The memory allocated here never gets released to not cause an exit time
+ // destructor.
+ static const ArrayValidateParams* validate_params = new ArrayValidateParams(
+ 0, false, new ArrayValidateParams(0, false, nullptr));
+ return validate_params;
+}
+
+template <typename MapKey>
+struct MapKeyValidateParamsFactory {
+ static const ArrayValidateParams* Get() {
+ return GetMapKeyValidateParamsDefault();
+ }
+};
+
+// For non-nullable strings only. (Which is OK; map keys can't be null.)
+template <>
+struct MapKeyValidateParamsFactory<mojo::internal::Array_Data<char>*> {
+ static const ArrayValidateParams* Get() {
+ return GetMapKeyValidateParamsForStrings();
+ }
+};
+
+// Map serializes into a struct which has two arrays as struct fields, the keys
+// and the values.
+template <typename Key, typename Value>
+class Map_Data {
+ public:
+ static Map_Data* New(Buffer* buf) {
+ return new (buf->Allocate(sizeof(Map_Data))) Map_Data();
+ }
+
+ static bool Validate(const void* data,
+ BoundsChecker* bounds_checker,
+ const ArrayValidateParams* value_validate_params) {
+ if (!data)
+ return true;
+
+ if (!ValidateStructHeaderAndClaimMemory(data, bounds_checker))
+ return false;
+
+ const Map_Data* object = static_cast<const Map_Data*>(data);
+ if (object->header_.num_bytes != sizeof(Map_Data) ||
+ object->header_.version != 0) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+
+ if (!ValidateEncodedPointer(&object->keys.offset)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER);
+ return false;
+ }
+ if (!object->keys.offset) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null key array in map struct");
+ return false;
+ }
+ const ArrayValidateParams* key_validate_params =
+ MapKeyValidateParamsFactory<Key>::Get();
+ if (!Array_Data<Key>::Validate(DecodePointerRaw(&object->keys.offset),
+ bounds_checker, key_validate_params)) {
+ return false;
+ }
+
+ if (!ValidateEncodedPointer(&object->values.offset)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER);
+ return false;
+ }
+ if (!object->values.offset) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null value array in map struct");
+ return false;
+ }
+ if (!Array_Data<Value>::Validate(DecodePointerRaw(&object->values.offset),
+ bounds_checker, value_validate_params)) {
+ return false;
+ }
+
+ const ArrayHeader* key_header =
+ static_cast<const ArrayHeader*>(DecodePointerRaw(&object->keys.offset));
+ const ArrayHeader* value_header = static_cast<const ArrayHeader*>(
+ DecodePointerRaw(&object->values.offset));
+ if (key_header->num_elements != value_header->num_elements) {
+ ReportValidationError(VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP);
+ return false;
+ }
+
+ return true;
+ }
+
+ StructHeader header_;
+
+ ArrayPointer<Key> keys;
+ ArrayPointer<Value> values;
+
+ void EncodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+ Encode(&keys, handles);
+ Encode(&values, handles);
+ }
+
+ void DecodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+ Decode(&keys, handles);
+ Decode(&values, handles);
+ }
+
+ private:
+ Map_Data() {
+ header_.num_bytes = sizeof(*this);
+ header_.version = 0;
+ }
+ ~Map_Data() = delete;
+};
+static_assert(sizeof(Map_Data<char, char>) == 24, "Bad sizeof(Map_Data)");
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_DATA_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/map_internal.h b/mojo/public/cpp/bindings/lib/map_internal.h
new file mode 100644
index 0000000..84f927c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/map_internal.h
@@ -0,0 +1,220 @@
+// Copyright 2014 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_MAP_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_
+
+#include <map>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Key, typename Value, bool kValueIsMoveOnlyType>
+struct MapTraits {};
+
+// Defines traits of a map for which Value is not a move-only type.
+template <typename Key, typename Value>
+struct MapTraits<Key, Value, false> {
+ // Map keys can't be move only types.
+ static_assert(!internal::IsMoveOnlyType<Key>::value,
+ "Map keys cannot be move only types.");
+
+ typedef Key KeyStorageType;
+ typedef Key& KeyRefType;
+ typedef const Key& KeyConstRefType;
+ typedef KeyConstRefType KeyForwardType;
+
+ typedef Value ValueStorageType;
+ typedef Value& ValueRefType;
+ typedef const Value& ValueConstRefType;
+ typedef ValueConstRefType ValueForwardType;
+
+ static inline void InitializeFrom(
+ std::map<KeyStorageType, ValueStorageType>* m,
+ mojo::Array<Key> keys,
+ mojo::Array<Value> values) {
+ for (size_t i = 0; i < keys.size(); ++i)
+ Insert(m, keys[i], values[i]);
+ }
+ static inline void Decompose(std::map<KeyStorageType, ValueStorageType>* m,
+ mojo::Array<Key>* keys,
+ mojo::Array<Value>* values) {
+ keys->resize(m->size());
+ values->resize(m->size());
+ int i = 0;
+ for (typename std::map<KeyStorageType, ValueStorageType>::iterator
+ it = m->begin();
+ it != m->end();
+ ++it, ++i) {
+ (*keys)[i] = it->first;
+ (*values)[i] = it->second;
+ }
+ }
+ static inline void Finalize(std::map<KeyStorageType, ValueStorageType>* m) {}
+ static inline ValueRefType at(std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key) {
+ // We don't have C++11 library support yet, so we have to emulate the crash
+ // on a non-existent key.
+ auto it = m->find(key);
+ MOJO_CHECK(it != m->end());
+ return it->second;
+ }
+ static inline ValueConstRefType at(
+ const std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key) {
+ // We don't have C++11 library support yet, so we have to emulate the crash
+ // on a non-existent key.
+ auto it = m->find(key);
+ MOJO_CHECK(it != m->end());
+ return it->second;
+ }
+ static inline ValueRefType GetOrInsert(
+ std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key) {
+ // This is the backing for the index operator (operator[]).
+ return (*m)[key];
+ }
+ static inline void Insert(std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key,
+ ValueForwardType value) {
+ m->insert(std::make_pair(key, value));
+ }
+ static inline KeyConstRefType GetKey(
+ const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
+ it) {
+ return it->first;
+ }
+ static inline ValueConstRefType GetValue(
+ const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
+ it) {
+ return it->second;
+ }
+ static inline ValueRefType GetValue(
+ const typename std::map<KeyStorageType, ValueStorageType>::iterator& it) {
+ return it->second;
+ }
+ static inline void Clone(
+ const std::map<KeyStorageType, ValueStorageType>& src,
+ std::map<KeyStorageType, ValueStorageType>* dst) {
+ dst->clear();
+ for (auto it = src.begin(); it != src.end(); ++it)
+ dst->insert(*it);
+ }
+};
+
+// Defines traits of a map for which Value is a move-only type.
+template <typename Key, typename Value>
+struct MapTraits<Key, Value, true> {
+ // Map keys can't be move only types.
+ static_assert(!internal::IsMoveOnlyType<Key>::value,
+ "Map keys cannot be move only types.");
+
+ typedef Key KeyStorageType;
+ typedef Key& KeyRefType;
+ typedef const Key& KeyConstRefType;
+ typedef KeyConstRefType KeyForwardType;
+
+ struct ValueStorageType {
+ // Make 8-byte aligned.
+ char buf[sizeof(Value) + (8 - (sizeof(Value) % 8)) % 8];
+ };
+ typedef Value& ValueRefType;
+ typedef const Value& ValueConstRefType;
+ typedef Value ValueForwardType;
+
+ static inline void InitializeFrom(
+ std::map<KeyStorageType, ValueStorageType>* m,
+ mojo::Array<Key> keys,
+ mojo::Array<Value> values) {
+ for (size_t i = 0; i < keys.size(); ++i)
+ Insert(m, keys[i], values[i]);
+ }
+ static inline void Decompose(std::map<KeyStorageType, ValueStorageType>* m,
+ mojo::Array<Key>* keys,
+ mojo::Array<Value>* values) {
+ keys->resize(m->size());
+ values->resize(m->size());
+ int i = 0;
+ for (typename std::map<KeyStorageType, ValueStorageType>::iterator
+ it = m->begin();
+ it != m->end();
+ ++it, ++i) {
+ (*keys)[i] = it->first;
+ (*values)[i] = GetValue(it).Pass();
+ }
+ }
+ static inline void Finalize(std::map<KeyStorageType, ValueStorageType>* m) {
+ for (auto& pair : *m)
+ reinterpret_cast<Value*>(pair.second.buf)->~Value();
+ }
+ static inline ValueRefType at(std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key) {
+ // We don't have C++11 library support yet, so we have to emulate the crash
+ // on a non-existent key.
+ auto it = m->find(key);
+ MOJO_CHECK(it != m->end());
+ return GetValue(it);
+ }
+ static inline ValueConstRefType at(
+ const std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key) {
+ // We don't have C++11 library support yet, so we have to emulate the crash
+ // on a non-existent key.
+ auto it = m->find(key);
+ MOJO_CHECK(it != m->end());
+ return GetValue(it);
+ }
+ static inline ValueRefType GetOrInsert(
+ std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key) {
+ // This is the backing for the index operator (operator[]).
+ auto it = m->find(key);
+ if (it == m->end()) {
+ it = m->insert(std::make_pair(key, ValueStorageType())).first;
+ new (it->second.buf) Value();
+ }
+
+ return GetValue(it);
+ }
+ static inline void Insert(std::map<KeyStorageType, ValueStorageType>* m,
+ KeyForwardType key,
+ ValueRefType value) {
+ // STL insert() doesn't insert |value| if |key| is already part of |m|. We
+ // have to use operator[] to initialize into the storage buffer, but we
+ // have to do a manual check so that we don't overwrite an existing object.
+ auto it = m->find(key);
+ if (it == m->end())
+ new ((*m)[key].buf) Value(value.Pass());
+ }
+ static inline KeyConstRefType GetKey(
+ const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
+ it) {
+ return it->first;
+ }
+ static inline ValueConstRefType GetValue(
+ const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
+ it) {
+ return *reinterpret_cast<const Value*>(it->second.buf);
+ }
+ static inline ValueRefType GetValue(
+ const typename std::map<KeyStorageType, ValueStorageType>::iterator& it) {
+ return *reinterpret_cast<Value*>(it->second.buf);
+ }
+ static inline void Clone(
+ const std::map<KeyStorageType, ValueStorageType>& src,
+ std::map<KeyStorageType, ValueStorageType>* dst) {
+ Finalize(dst);
+ dst->clear();
+ for (auto it = src.begin(); it != src.end(); ++it)
+ new ((*dst)[it->first].buf) Value(GetValue(it).Clone());
+ }
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h
new file mode 100644
index 0000000..6014b36
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/map_serialization.h
@@ -0,0 +1,182 @@
+// Copyright 2014 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_MAP_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/map_internal.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/map.h"
+
+namespace mojo {
+
+template <typename Key, typename Value>
+inline size_t GetSerializedSize_(const Map<Key, Value>& input);
+
+template <typename ValidateParams, typename E, typename F>
+inline void SerializeArray_(
+ Array<E> input,
+ internal::Buffer* buf,
+ internal::Array_Data<F>** output,
+ const internal::ArrayValidateParams* validate_params);
+
+namespace internal {
+
+template <typename MapType,
+ typename DataType,
+ bool value_is_move_only_type = IsMoveOnlyType<MapType>::value,
+ bool is_union =
+ IsUnionDataType<typename RemovePointer<DataType>::type>::value>
+struct MapSerializer;
+
+template <typename MapType, typename DataType>
+struct MapSerializer<MapType, DataType, false, false> {
+ static size_t GetBaseArraySize(size_t count) {
+ return Align(count * sizeof(DataType));
+ }
+ static size_t GetItemSize(const MapType& item) { return 0; }
+};
+
+template <>
+struct MapSerializer<bool, bool, false, false> {
+ static size_t GetBaseArraySize(size_t count) {
+ return Align((count + 7) / 8);
+ }
+ static size_t GetItemSize(bool item) { return 0; }
+};
+
+template <typename H>
+struct MapSerializer<ScopedHandleBase<H>, H, true, false> {
+ static size_t GetBaseArraySize(size_t count) {
+ return Align(count * sizeof(H));
+ }
+ static size_t GetItemSize(const ScopedHandleBase<H>& item) { return 0; }
+};
+
+// This template must only apply to pointer mojo entity (structs and arrays).
+// This is done by ensuring that WrapperTraits<S>::DataType is a pointer.
+template <typename S>
+struct MapSerializer<
+ S,
+ typename EnableIf<IsPointer<typename WrapperTraits<S>::DataType>::value,
+ typename WrapperTraits<S>::DataType>::type,
+ true,
+ false> {
+ typedef
+ typename RemovePointer<typename WrapperTraits<S>::DataType>::type S_Data;
+ static size_t GetBaseArraySize(size_t count) {
+ return count * sizeof(StructPointer<S_Data>);
+ }
+ static size_t GetItemSize(const S& item) { return GetSerializedSize_(item); }
+};
+
+template <typename U, typename U_Data>
+struct MapSerializer<U, U_Data*, true, true> {
+ static size_t GetBaseArraySize(size_t count) {
+ return count * sizeof(U_Data);
+ }
+ static size_t GetItemSize(const U& item) {
+ return GetSerializedSize_(item, true);
+ }
+};
+
+template <>
+struct MapSerializer<String, String_Data*, false, false> {
+ static size_t GetBaseArraySize(size_t count) {
+ return count * sizeof(StringPointer);
+ }
+ static size_t GetItemSize(const String& item) {
+ return GetSerializedSize_(item);
+ }
+};
+
+} // namespace internal
+
+// TODO(erg): This can't go away yet. We still need to calculate out the size
+// of a struct header, and two arrays.
+template <typename MapKey, typename MapValue>
+inline size_t GetSerializedSize_(const Map<MapKey, MapValue>& input) {
+ if (!input)
+ return 0;
+ typedef typename internal::WrapperTraits<MapKey>::DataType DataKey;
+ typedef typename internal::WrapperTraits<MapValue>::DataType DataValue;
+
+ size_t count = input.size();
+ size_t struct_overhead = sizeof(mojo::internal::Map_Data<DataKey, DataValue>);
+ size_t key_base_size =
+ sizeof(internal::ArrayHeader) +
+ internal::MapSerializer<MapKey, DataKey>::GetBaseArraySize(count);
+ size_t value_base_size =
+ sizeof(internal::ArrayHeader) +
+ internal::MapSerializer<MapValue, DataValue>::GetBaseArraySize(count);
+
+ size_t key_data_size = 0;
+ size_t value_data_size = 0;
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ key_data_size +=
+ internal::MapSerializer<MapKey, DataKey>::GetItemSize(it.GetKey());
+ value_data_size +=
+ internal::MapSerializer<MapValue, DataValue>::GetItemSize(
+ it.GetValue());
+ }
+
+ return struct_overhead + key_base_size + key_data_size + value_base_size +
+ value_data_size;
+}
+
+// We don't need an ArrayValidateParams instance for key validation since
+// we can deduce it from the Key type. (which can only be primitive types or
+// non-nullable strings.)
+template <typename MapKey,
+ typename MapValue,
+ typename DataKey,
+ typename DataValue>
+inline void SerializeMap_(
+ Map<MapKey, MapValue> input,
+ internal::Buffer* buf,
+ internal::Map_Data<DataKey, DataValue>** output,
+ const internal::ArrayValidateParams* value_validate_params) {
+ if (input) {
+ internal::Map_Data<DataKey, DataValue>* result =
+ internal::Map_Data<DataKey, DataValue>::New(buf);
+ if (result) {
+ Array<MapKey> keys;
+ Array<MapValue> values;
+ input.DecomposeMapTo(&keys, &values);
+ const internal::ArrayValidateParams* key_validate_params =
+ internal::MapKeyValidateParamsFactory<DataKey>::Get();
+ SerializeArray_(keys.Pass(), buf, &result->keys.ptr, key_validate_params);
+ SerializeArray_(values.Pass(), buf, &result->values.ptr,
+ value_validate_params);
+ }
+ *output = result;
+ } else {
+ *output = nullptr;
+ }
+}
+
+template <typename MapKey,
+ typename MapValue,
+ typename DataKey,
+ typename DataValue>
+inline void Deserialize_(internal::Map_Data<DataKey, DataValue>* input,
+ Map<MapKey, MapValue>* output) {
+ if (input) {
+ Array<MapKey> keys;
+ Array<MapValue> values;
+
+ Deserialize_(input->keys.ptr, &keys);
+ Deserialize_(input->values.ptr, &values);
+
+ *output = Map<MapKey, MapValue>(keys.Pass(), values.Pass());
+ } else {
+ output->reset();
+ }
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
new file mode 100644
index 0000000..6b563e7
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -0,0 +1,105 @@
+// Copyright 2013 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/message.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+Message::Message() {
+ Initialize();
+}
+
+Message::~Message() {
+ FreeDataAndCloseHandles();
+}
+
+void Message::Reset() {
+ FreeDataAndCloseHandles();
+
+ handles_.clear();
+ Initialize();
+}
+
+void Message::AllocData(uint32_t num_bytes) {
+ MOJO_DCHECK(!data_);
+ data_num_bytes_ = num_bytes;
+ data_ = static_cast<internal::MessageData*>(calloc(num_bytes, 1));
+}
+
+void Message::AllocUninitializedData(uint32_t num_bytes) {
+ MOJO_DCHECK(!data_);
+ data_num_bytes_ = num_bytes;
+ data_ = static_cast<internal::MessageData*>(malloc(num_bytes));
+}
+
+void Message::MoveTo(Message* destination) {
+ MOJO_DCHECK(this != destination);
+
+ destination->FreeDataAndCloseHandles();
+
+ // No copy needed.
+ destination->data_num_bytes_ = data_num_bytes_;
+ destination->data_ = data_;
+ std::swap(destination->handles_, handles_);
+
+ handles_.clear();
+ Initialize();
+}
+
+void Message::Initialize() {
+ data_num_bytes_ = 0;
+ data_ = nullptr;
+}
+
+void Message::FreeDataAndCloseHandles() {
+ free(data_);
+
+ for (std::vector<Handle>::iterator it = handles_.begin();
+ it != handles_.end(); ++it) {
+ if (it->is_valid())
+ CloseRaw(*it);
+ }
+}
+
+MojoResult ReadAndDispatchMessage(MessagePipeHandle handle,
+ MessageReceiver* receiver,
+ bool* receiver_result) {
+ MojoResult rv;
+
+ uint32_t num_bytes = 0, num_handles = 0;
+ rv = ReadMessageRaw(handle,
+ nullptr,
+ &num_bytes,
+ nullptr,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED)
+ return rv;
+
+ Message message;
+ message.AllocUninitializedData(num_bytes);
+ message.mutable_handles()->resize(num_handles);
+
+ rv = ReadMessageRaw(
+ handle,
+ message.mutable_data(),
+ &num_bytes,
+ message.mutable_handles()->empty()
+ ? nullptr
+ : reinterpret_cast<MojoHandle*>(&message.mutable_handles()->front()),
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (receiver && rv == MOJO_RESULT_OK)
+ *receiver_result = receiver->Accept(&message);
+
+ return rv;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc
new file mode 100644
index 0000000..25f1862
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 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/lib/message_builder.h"
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Header>
+void Allocate(Buffer* buf, Header** header) {
+ *header = static_cast<Header*>(buf->Allocate(sizeof(Header)));
+ (*header)->num_bytes = sizeof(Header);
+}
+
+MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) {
+ Initialize(sizeof(MessageHeader) + payload_size);
+
+ MessageHeader* header;
+ Allocate(&buf_, &header);
+ header->version = 0;
+ header->name = name;
+}
+
+MessageBuilder::~MessageBuilder() {
+}
+
+MessageBuilder::MessageBuilder() {}
+
+void MessageBuilder::Initialize(size_t size) {
+ message_.AllocData(static_cast<uint32_t>(Align(size)));
+ buf_.Initialize(message_.mutable_data(), message_.data_num_bytes());
+}
+
+MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t flags,
+ uint64_t request_id) {
+ Initialize(sizeof(MessageHeaderWithRequestID) + payload_size);
+ MessageHeaderWithRequestID* header;
+ Allocate(&buf_, &header);
+ header->version = 1;
+ header->name = name;
+ header->flags = flags;
+ header->request_id = request_id;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h
new file mode 100644
index 0000000..86ae71d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -0,0 +1,68 @@
+// Copyright 2013 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_MESSAGE_BUILDER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+class MessageBuilder {
+ public:
+ MessageBuilder(uint32_t name, size_t payload_size);
+ ~MessageBuilder();
+
+ Buffer* buffer() { return &buf_; }
+ Message* message() { return &message_; }
+
+ protected:
+ MessageBuilder();
+ void Initialize(size_t size);
+
+ Message message_;
+ FixedBuffer buf_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessageBuilder);
+};
+
+class MessageWithRequestIDBuilder : public MessageBuilder {
+ public:
+ MessageWithRequestIDBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t flags,
+ uint64_t request_id);
+};
+
+class RequestMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+ RequestMessageBuilder(uint32_t name, size_t payload_size)
+ : MessageWithRequestIDBuilder(name,
+ payload_size,
+ kMessageExpectsResponse,
+ 0) {}
+};
+
+class ResponseMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+ ResponseMessageBuilder(uint32_t name,
+ size_t payload_size,
+ uint64_t request_id)
+ : MessageWithRequestIDBuilder(name,
+ payload_size,
+ kMessageIsResponse,
+ request_id) {}
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
diff --git a/mojo/public/cpp/bindings/lib/message_filter.cc b/mojo/public/cpp/bindings/lib/message_filter.cc
new file mode 100644
index 0000000..b09f40d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_filter.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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/message_filter.h"
+
+namespace mojo {
+
+MessageFilter::MessageFilter(MessageReceiver* sink) : sink_(sink) {
+}
+
+MessageFilter::~MessageFilter() {
+}
+
+PassThroughFilter::PassThroughFilter(MessageReceiver* sink)
+ : MessageFilter(sink) {
+}
+
+bool PassThroughFilter::Accept(Message* message) {
+ return sink_->Accept(message);
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc
new file mode 100644
index 0000000..940b15c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 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/lib/message_header_validator.h"
+
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+bool IsValidMessageHeader(const MessageHeader* header) {
+ // NOTE: Our goal is to preserve support for future extension of the message
+ // header. If we encounter fields we do not understand, we must ignore them.
+
+ // Extra validation of the struct header:
+ if (header->version == 0) {
+ if (header->num_bytes != sizeof(MessageHeader)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->version == 1) {
+ if (header->num_bytes != sizeof(MessageHeaderWithRequestID)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->version > 1) {
+ if (header->num_bytes < sizeof(MessageHeaderWithRequestID)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ }
+
+ // Validate flags (allow unknown bits):
+
+ // These flags require a RequestID.
+ if (header->version < 1 && ((header->flags & kMessageExpectsResponse) ||
+ (header->flags & kMessageIsResponse))) {
+ ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID);
+ return false;
+ }
+
+ // These flags are mutually exclusive.
+ if ((header->flags & kMessageExpectsResponse) &&
+ (header->flags & kMessageIsResponse)) {
+ ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink)
+ : MessageFilter(sink) {
+}
+
+bool MessageHeaderValidator::Accept(Message* message) {
+ // Pass 0 as number of handles because we don't expect any in the header, even
+ // if |message| contains handles.
+ BoundsChecker bounds_checker(message->data(), message->data_num_bytes(), 0);
+
+ if (!ValidateStructHeaderAndClaimMemory(message->data(), &bounds_checker))
+ return false;
+
+ if (!IsValidMessageHeader(message->header()))
+ return false;
+
+ return sink_->Accept(message);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.h b/mojo/public/cpp/bindings/lib/message_header_validator.h
new file mode 100644
index 0000000..bccef1f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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_MESSAGE_HEADER_VALIDATOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+namespace internal {
+
+class MessageHeaderValidator : public MessageFilter {
+ public:
+ explicit MessageHeaderValidator(MessageReceiver* sink = nullptr);
+
+ bool Accept(Message* message) override;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h
new file mode 100644
index 0000000..97b9619
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_internal.h
@@ -0,0 +1,50 @@
+// Copyright 2014 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_MESSAGE_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
+namespace mojo {
+namespace internal {
+
+#pragma pack(push, 1)
+
+enum { kMessageExpectsResponse = 1 << 0, kMessageIsResponse = 1 << 1 };
+
+struct MessageHeader : internal::StructHeader {
+ // Interface ID for identifying multiple interfaces running on the same
+ // message pipe.
+ uint32_t interface_id;
+ // Message name, which is scoped to the interface that the message belongs to.
+ uint32_t name;
+ // 0 or either of the enum values defined above.
+ uint32_t flags;
+ // Unused padding to make the struct size a multiple of 8 bytes.
+ uint32_t padding;
+};
+static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)");
+
+struct MessageHeaderWithRequestID : MessageHeader {
+ // Only used if either kMessageExpectsResponse or kMessageIsResponse is set in
+ // order to match responses with corresponding requests.
+ uint64_t request_id;
+};
+static_assert(sizeof(MessageHeaderWithRequestID) == 32,
+ "Bad sizeof(MessageHeaderWithRequestID)");
+
+struct MessageData {
+ MessageHeader header;
+};
+
+static_assert(sizeof(MessageData) == sizeof(MessageHeader),
+ "Bad sizeof(MessageData)");
+
+#pragma pack(pop)
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
diff --git a/mojo/public/cpp/bindings/lib/no_interface.cc b/mojo/public/cpp/bindings/lib/no_interface.cc
new file mode 100644
index 0000000..9e0945c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/no_interface.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 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/no_interface.h"
+
+namespace mojo {
+
+const char* NoInterface::Name_ = "mojo::NoInterface";
+
+bool NoInterfaceStub::Accept(Message* message) {
+ return false;
+}
+
+bool NoInterfaceStub::AcceptWithResponder(Message* message,
+ MessageReceiver* responder) {
+ return false;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc
new file mode 100644
index 0000000..fff72b4
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 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/lib/router.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+class ResponderThunk : public MessageReceiverWithStatus {
+ public:
+ explicit ResponderThunk(const SharedData<Router*>& router)
+ : router_(router), accept_was_invoked_(false) {}
+ ~ResponderThunk() override {
+ if (!accept_was_invoked_) {
+ // The Mojo application handled a message that was expecting a response
+ // but did not send a response.
+ Router* router = router_.value();
+ if (router) {
+ // We raise an error to signal the calling application that an error
+ // condition occurred. Without this the calling application would have
+ // no way of knowing it should stop waiting for a response.
+ router->RaiseError();
+ }
+ }
+ }
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override {
+ accept_was_invoked_ = true;
+ MOJO_DCHECK(message->has_flag(kMessageIsResponse));
+
+ bool result = false;
+
+ Router* router = router_.value();
+ if (router)
+ result = router->Accept(message);
+
+ return result;
+ }
+
+ // MessageReceiverWithStatus implementation:
+ bool IsValid() override {
+ Router* router = router_.value();
+ return router && !router->encountered_error() && router->is_valid();
+ }
+
+ private:
+ SharedData<Router*> router_;
+ bool accept_was_invoked_;
+};
+
+// ----------------------------------------------------------------------------
+
+Router::HandleIncomingMessageThunk::HandleIncomingMessageThunk(Router* router)
+ : router_(router) {
+}
+
+Router::HandleIncomingMessageThunk::~HandleIncomingMessageThunk() {
+}
+
+bool Router::HandleIncomingMessageThunk::Accept(Message* message) {
+ return router_->HandleIncomingMessage(message);
+}
+
+// ----------------------------------------------------------------------------
+
+Router::Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ const MojoAsyncWaiter* waiter)
+ : thunk_(this),
+ filters_(filters.Pass()),
+ connector_(message_pipe.Pass(), waiter),
+ weak_self_(this),
+ incoming_receiver_(nullptr),
+ next_request_id_(0),
+ testing_mode_(false) {
+ filters_.SetSink(&thunk_);
+ connector_.set_incoming_receiver(filters_.GetHead());
+}
+
+Router::~Router() {
+ weak_self_.set_value(nullptr);
+
+ for (auto& pair : responders_)
+ delete pair.second;
+}
+
+bool Router::Accept(Message* message) {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ MOJO_DCHECK(!message->has_flag(kMessageExpectsResponse));
+ return connector_.Accept(message);
+}
+
+bool Router::AcceptWithResponder(Message* message, MessageReceiver* responder) {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ MOJO_DCHECK(message->has_flag(kMessageExpectsResponse));
+
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ uint64_t request_id = next_request_id_++;
+ if (request_id == 0)
+ request_id = next_request_id_++;
+
+ message->set_request_id(request_id);
+ if (!connector_.Accept(message))
+ return false;
+
+ // We assume ownership of |responder|.
+ responders_[request_id] = responder;
+ return true;
+}
+
+void Router::EnableTestingMode() {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ testing_mode_ = true;
+ connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool Router::HandleIncomingMessage(Message* message) {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ if (message->has_flag(kMessageExpectsResponse)) {
+ if (!incoming_receiver_)
+ return false;
+
+ MessageReceiverWithStatus* responder = new ResponderThunk(weak_self_);
+ bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+ if (!ok)
+ delete responder;
+ return ok;
+
+ } else if (message->has_flag(kMessageIsResponse)) {
+ uint64_t request_id = message->request_id();
+ ResponderMap::iterator it = responders_.find(request_id);
+ if (it == responders_.end()) {
+ MOJO_DCHECK(testing_mode_);
+ return false;
+ }
+ MessageReceiver* responder = it->second;
+ responders_.erase(it);
+ bool ok = responder->Accept(message);
+ delete responder;
+ return ok;
+ } else {
+ if (!incoming_receiver_)
+ return false;
+
+ return incoming_receiver_->Accept(message);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/router.h b/mojo/public/cpp/bindings/lib/router.h
new file mode 100644
index 0000000..1697610
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.h
@@ -0,0 +1,141 @@
+// Copyright 2014 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_ROUTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+
+#include <map>
+
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/shared_data.h"
+#include "mojo/public/cpp/bindings/lib/thread_checker.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+class Router : public MessageReceiverWithResponder {
+ public:
+ Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+ ~Router() override;
+
+ // Sets the receiver to handle messages read from the message pipe that do
+ // not have the kMessageIsResponse flag set.
+ void set_incoming_receiver(MessageReceiverWithResponderStatus* receiver) {
+ incoming_receiver_ = receiver;
+ }
+
+ // Sets the error handler to receive notifications when an error is
+ // encountered while reading from the pipe or waiting to read from the pipe.
+ void set_connection_error_handler(const Closure& error_handler) {
+ connector_.set_connection_error_handler(error_handler);
+ }
+
+ // Returns true if an error was encountered while reading from the pipe or
+ // waiting to read from the pipe.
+ bool encountered_error() const {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.encountered_error();
+ }
+
+ // Is the router bound to a MessagePipe handle?
+ bool is_valid() const {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.is_valid();
+ }
+
+ // Please note that this method shouldn't be called unless it results from an
+ // explicit request of the user of bindings (e.g., the user sets an
+ // InterfacePtr to null or closes a Binding).
+ void CloseMessagePipe() {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.CloseMessagePipe();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.PassMessagePipe();
+ }
+
+ void RaiseError() {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.RaiseError();
+ }
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override;
+ bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override;
+
+ // Blocks the current thread until the first incoming method call, i.e.,
+ // either a call to a client method or a callback method, or |deadline|.
+ bool WaitForIncomingMessage(MojoDeadline deadline) {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.WaitForIncomingMessage(deadline);
+ }
+
+ // See Binding for details of pause/resume.
+ void PauseIncomingMethodCallProcessing() {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.ResumeIncomingMethodCallProcessing();
+ }
+
+ // Sets this object to testing mode.
+ // In testing mode:
+ // - the object is more tolerant of unrecognized response messages;
+ // - the connector continues working after seeing errors from its incoming
+ // receiver.
+ void EnableTestingMode();
+
+ MessagePipeHandle handle() const { return connector_.handle(); }
+
+ // Returns true if this Router has any pending callbacks.
+ bool has_pending_responders() const {
+ MOJO_DCHECK(thread_checker_.CalledOnValidThread());
+ return !responders_.empty();
+ }
+
+ private:
+ typedef std::map<uint64_t, MessageReceiver*> ResponderMap;
+
+ class HandleIncomingMessageThunk : public MessageReceiver {
+ public:
+ HandleIncomingMessageThunk(Router* router);
+ ~HandleIncomingMessageThunk() override;
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override;
+
+ private:
+ Router* router_;
+ };
+
+ bool HandleIncomingMessage(Message* message);
+
+ HandleIncomingMessageThunk thunk_;
+ FilterChain filters_;
+ Connector connector_;
+ SharedData<Router*> weak_self_;
+ MessageReceiverWithResponderStatus* incoming_receiver_;
+ // Maps from the id of a response to the MessageReceiver that handles the
+ // response.
+ ResponderMap responders_;
+ uint64_t next_request_id_;
+ bool testing_mode_;
+ ThreadChecker thread_checker_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
diff --git a/mojo/public/cpp/bindings/lib/shared_data.h b/mojo/public/cpp/bindings/lib/shared_data.h
new file mode 100644
index 0000000..2676224
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/shared_data.h
@@ -0,0 +1,83 @@
+// Copyright 2014 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_SHARED_DATA_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
+
+#include <assert.h>
+
+#include "mojo/public/cpp/bindings/lib/thread_checker.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Used to allocate an instance of T that can be shared via reference counting.
+template <typename T>
+class SharedData {
+ public:
+ ~SharedData() { holder_->Release(); }
+
+ SharedData() : holder_(new Holder()) {}
+
+ explicit SharedData(const T& value) : holder_(new Holder(value)) {}
+
+ SharedData(const SharedData<T>& other) : holder_(other.holder_) {
+ holder_->Retain();
+ }
+
+ SharedData<T>& operator=(const SharedData<T>& other) {
+ if (other.holder_ == holder_)
+ return *this;
+ holder_->Release();
+ holder_ = other.holder_;
+ holder_->Retain();
+ return *this;
+ }
+
+ void reset() {
+ holder_->Release();
+ holder_ = new Holder();
+ }
+
+ void reset(const T& value) {
+ holder_->Release();
+ holder_ = new Holder(value);
+ }
+
+ void set_value(const T& value) { holder_->value = value; }
+ T* mutable_value() { return &holder_->value; }
+ const T& value() const { return holder_->value; }
+
+ private:
+ class Holder {
+ public:
+ Holder() : value(), ref_count_(1) {}
+ Holder(const T& value) : value(value), ref_count_(1) {}
+
+ void Retain() {
+ assert(thread_checker_.CalledOnValidThread());
+ ++ref_count_;
+ }
+ void Release() {
+ assert(thread_checker_.CalledOnValidThread());
+ if (--ref_count_ == 0)
+ delete this;
+ }
+
+ T value;
+
+ private:
+ int ref_count_;
+ ThreadChecker thread_checker_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Holder);
+ };
+
+ Holder* holder_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
diff --git a/mojo/public/cpp/bindings/lib/shared_ptr.h b/mojo/public/cpp/bindings/lib/shared_ptr.h
new file mode 100644
index 0000000..37c8735
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/shared_ptr.h
@@ -0,0 +1,57 @@
+// Copyright 2014 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_SHARED_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
+
+#include "mojo/public/cpp/bindings/lib/shared_data.h"
+
+namespace mojo {
+namespace internal {
+
+// Used to manage a heap-allocated instance of P that can be shared via
+// reference counting. When the last reference is dropped, the instance is
+// deleted.
+template <typename P>
+class SharedPtr {
+ public:
+ SharedPtr() {}
+
+ explicit SharedPtr(P* ptr) { impl_.mutable_value()->ptr = ptr; }
+
+ // Default copy-constructor and assignment operator are OK.
+
+ P* get() { return impl_.value().ptr; }
+ const P* get() const { return impl_.value().ptr; }
+
+ void reset() { impl_.reset(); }
+
+ P* operator->() { return get(); }
+ const P* operator->() const { return get(); }
+
+ private:
+ class Impl {
+ public:
+ ~Impl() {
+ if (ptr)
+ delete ptr;
+ }
+
+ Impl() : ptr(nullptr) {}
+
+ Impl(P* ptr) : ptr(ptr) {}
+
+ P* ptr;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Impl);
+ };
+
+ SharedData<Impl> impl_;
+};
+
+} // namespace mojo
+} // namespace internal
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.cc b/mojo/public/cpp/bindings/lib/string_serialization.cc
new file mode 100644
index 0000000..e29d6f8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 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/lib/string_serialization.h"
+
+#include <string.h>
+
+namespace mojo {
+
+size_t GetSerializedSize_(const String& input) {
+ if (!input)
+ return 0;
+ return internal::Align(sizeof(internal::String_Data) + input.size());
+}
+
+void Serialize_(const String& input,
+ internal::Buffer* buf,
+ internal::String_Data** output) {
+ if (input) {
+ internal::String_Data* result =
+ internal::String_Data::New(input.size(), buf);
+ if (result)
+ memcpy(result->storage(), input.data(), input.size());
+ *output = result;
+ } else {
+ *output = nullptr;
+ }
+}
+
+void Deserialize_(internal::String_Data* input, String* output) {
+ if (input) {
+ String result(input->storage(), input->size());
+ result.Swap(output);
+ } else {
+ output->reset();
+ }
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
new file mode 100644
index 0000000..0118742
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -0,0 +1,21 @@
+// Copyright 2014 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_STRING_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/string.h"
+
+namespace mojo {
+
+size_t GetSerializedSize_(const String& input);
+void Serialize_(const String& input,
+ internal::Buffer* buffer,
+ internal::String_Data** output);
+void Deserialize_(internal::String_Data* input, String* output);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
diff --git a/mojo/public/cpp/bindings/lib/template_util.h b/mojo/public/cpp/bindings/lib/template_util.h
new file mode 100644
index 0000000..9a5788c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/template_util.h
@@ -0,0 +1,130 @@
+// Copyright 2014 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_TEMPLATE_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
+
+namespace mojo {
+namespace internal {
+
+template <class T, T v>
+struct IntegralConstant {
+ static const T value = v;
+};
+
+template <class T, T v>
+const T IntegralConstant<T, v>::value;
+
+typedef IntegralConstant<bool, true> TrueType;
+typedef IntegralConstant<bool, false> FalseType;
+
+template <class T>
+struct IsConst : FalseType {};
+template <class T>
+struct IsConst<const T> : TrueType {};
+
+template <class T>
+struct IsPointer : FalseType {};
+template <class T>
+struct IsPointer<T*> : TrueType {};
+
+template <bool B, typename T = void>
+struct EnableIf {};
+
+template <typename T>
+struct EnableIf<true, T> {
+ typedef T type;
+};
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+ YesType dummy[2];
+};
+
+// A helper template to determine if given type is non-const move-only-type,
+// i.e. if a value of the given type should be passed via .Pass() in a
+// destructive way.
+template <typename T>
+struct IsMoveOnlyType {
+ template <typename U>
+ static YesType Test(const typename U::MoveOnlyTypeForCPP03*);
+
+ template <typename U>
+ static NoType Test(...);
+
+ static const bool value =
+ sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value;
+};
+
+// Returns a reference to |t| when T is not a move-only type.
+template <typename T>
+typename EnableIf<!IsMoveOnlyType<T>::value, T>::type& Forward(T& t) {
+ return t;
+}
+
+// Returns the result of t.Pass() when T is a move-only type.
+template <typename T>
+typename EnableIf<IsMoveOnlyType<T>::value, T>::type Forward(T& t) {
+ return t.Pass();
+}
+
+// This goop is a trick used to implement a template that can be used to
+// determine if a given class is the base class of another given class.
+template <typename, typename>
+struct IsSame {
+ static bool const value = false;
+};
+template <typename A>
+struct IsSame<A, A> {
+ static bool const value = true;
+};
+template <typename Base, typename Derived>
+struct IsBaseOf {
+ private:
+ // This class doesn't work correctly with forward declarations.
+ // Because sizeof cannot be applied to incomplete types, this line prevents us
+ // from passing in forward declarations.
+ typedef char (*EnsureTypesAreComplete)[sizeof(Base) + sizeof(Derived)];
+
+ static Derived* CreateDerived();
+ static char(&Check(Base*))[1];
+ static char(&Check(...))[2];
+
+ public:
+ static bool const value = sizeof Check(CreateDerived()) == 1 &&
+ !IsSame<Base const, void const>::value;
+};
+
+template <class T>
+struct RemovePointer {
+ typedef T type;
+};
+template <class T>
+struct RemovePointer<T*> {
+ typedef T type;
+};
+
+template <template <typename...> class Template, typename T>
+struct IsSpecializationOf : FalseType {};
+
+template <template <typename...> class Template, typename... Args>
+struct IsSpecializationOf<Template, Template<Args...>> : TrueType {};
+
+template <bool B, typename T, typename F>
+struct Conditional {
+ typedef T type;
+};
+
+template <typename T, typename F>
+struct Conditional<false, T, F> {
+ typedef F type;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/thread_checker.h b/mojo/public/cpp/bindings/lib/thread_checker.h
new file mode 100644
index 0000000..da45d0a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/thread_checker.h
@@ -0,0 +1,37 @@
+// Copyright 2015 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_THREAD_CHECKER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+#if !defined(_WIN32)
+#include "mojo/public/cpp/bindings/lib/thread_checker_posix.h"
+#endif
+
+namespace mojo {
+namespace internal {
+
+class ThreadCheckerDoNothing {
+ public:
+ bool CalledOnValidThread() const MOJO_WARN_UNUSED_RESULT {
+ return true;
+ }
+};
+
+// ThreadChecker is a class used to verify that some methods of a class are
+// called from the same thread. It is meant to be a member variable of a class.
+// The entire lifecycle of a ThreadChecker must occur on a single thread.
+// In Release mode (without dcheck_always_on), ThreadChecker does nothing.
+#if !defined(_WIN32) && (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+using ThreadChecker = ThreadCheckerPosix;
+#else
+using ThreadChecker = ThreadCheckerDoNothing;
+#endif
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_H_
diff --git a/mojo/public/cpp/bindings/lib/thread_checker_posix.cc b/mojo/public/cpp/bindings/lib/thread_checker_posix.cc
new file mode 100644
index 0000000..c8a16e2
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/thread_checker_posix.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 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/lib/thread_checker_posix.h"
+
+#include <assert.h>
+
+namespace mojo {
+namespace internal {
+
+ThreadCheckerPosix::ThreadCheckerPosix() : attached_thread_id_(pthread_self()) {
+}
+
+ThreadCheckerPosix::~ThreadCheckerPosix() {
+ assert(CalledOnValidThread());
+}
+
+bool ThreadCheckerPosix::CalledOnValidThread() const {
+ return pthread_equal(pthread_self(), attached_thread_id_);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/thread_checker_posix.h b/mojo/public/cpp/bindings/lib/thread_checker_posix.h
new file mode 100644
index 0000000..4701b88
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/thread_checker_posix.h
@@ -0,0 +1,31 @@
+// Copyright 2015 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_THREAD_CHECKER_POSIX_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_POSIX_H_
+
+#include <pthread.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// An implementation of ThreadChecker for POSIX systems.
+class ThreadCheckerPosix {
+ public:
+ ThreadCheckerPosix();
+ ~ThreadCheckerPosix();
+
+ bool CalledOnValidThread() const MOJO_WARN_UNUSED_RESULT;
+ private:
+ const pthread_t attached_thread_id_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ThreadCheckerPosix);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_CHECKER_POSIX_H_
diff --git a/mojo/public/cpp/bindings/lib/union_accessor.h b/mojo/public/cpp/bindings/lib/union_accessor.h
new file mode 100644
index 0000000..821aede
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/union_accessor.h
@@ -0,0 +1,33 @@
+// Copyright 2015 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_UNION_ACCESSOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_
+
+namespace mojo {
+namespace internal {
+
+// When serializing and deserializing Unions, it is necessary to access
+// the private fields and methods of the Union. This allows us to do that
+// without leaking those same fields and methods in the Union interface.
+// All Union wrappers are friends of this class allowing such access.
+template <typename U>
+class UnionAccessor {
+ public:
+ explicit UnionAccessor(U* u) : u_(u) {}
+
+ typename U::Union_* data() { return &(u_->data_); }
+
+ typename U::Tag* tag() { return &(u_->tag_); }
+
+ void SwitchActive(typename U::Tag new_tag) { u_->SwitchActive(new_tag); }
+
+ private:
+ U* u_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_UNION_ACCESSOR_H_
diff --git a/mojo/public/cpp/bindings/lib/validate_params.h b/mojo/public/cpp/bindings/lib/validate_params.h
new file mode 100644
index 0000000..274a012
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validate_params.h
@@ -0,0 +1,49 @@
+// Copyright 2014 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_VALIDATE_PARAMS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+class ArrayValidateParams {
+ public:
+ // ArrayValidateParams takes ownership of |in_element_validate params|.
+ ArrayValidateParams(uint32_t in_expected_num_elements,
+ bool in_element_is_nullable,
+ ArrayValidateParams* in_element_validate_params)
+ : expected_num_elements(in_expected_num_elements),
+ element_is_nullable(in_element_is_nullable),
+ element_validate_params(in_element_validate_params) {}
+
+ ~ArrayValidateParams() {
+ if (element_validate_params)
+ delete element_validate_params;
+ }
+
+ // TODO(vtl): The members of this class shouldn't be public.
+
+ // If |expected_num_elements| is not 0, the array is expected to have exactly
+ // that number of elements.
+ uint32_t expected_num_elements;
+
+ // Whether the elements are nullable.
+ bool element_is_nullable;
+
+ // Validation information for elements. It is either a pointer to another
+ // instance of ArrayValidateParams (if elements are arrays or maps), or
+ // nullptr. In the case of maps, this is used to validate the value array.
+ ArrayValidateParams* element_validate_params;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ArrayValidateParams);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATE_PARAMS_H_
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc
new file mode 100644
index 0000000..ee58ed9
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 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/lib/validation_errors.h"
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+ValidationErrorObserverForTesting* g_validation_error_observer = nullptr;
+SerializationWarningObserverForTesting* g_serialization_warning_observer =
+ nullptr;
+
+} // namespace
+
+const char* ValidationErrorToString(ValidationError error) {
+ switch (error) {
+ case VALIDATION_ERROR_NONE:
+ return "VALIDATION_ERROR_NONE";
+ case VALIDATION_ERROR_MISALIGNED_OBJECT:
+ return "VALIDATION_ERROR_MISALIGNED_OBJECT";
+ case VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE:
+ return "VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE";
+ case VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER:
+ return "VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER";
+ case VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER:
+ return "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER";
+ case VALIDATION_ERROR_ILLEGAL_HANDLE:
+ return "VALIDATION_ERROR_ILLEGAL_HANDLE";
+ case VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE:
+ return "VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE";
+ case VALIDATION_ERROR_ILLEGAL_POINTER:
+ return "VALIDATION_ERROR_ILLEGAL_POINTER";
+ case VALIDATION_ERROR_UNEXPECTED_NULL_POINTER:
+ return "VALIDATION_ERROR_UNEXPECTED_NULL_POINTER";
+ case VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS";
+ case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID";
+ case VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD";
+ case VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP:
+ return "VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP";
+ case VALIDATION_ERROR_UNKNOWN_UNION_TAG:
+ return "VALIDATION_ERROR_UNKNOWN_UNION_TAG";
+ }
+
+ return "Unknown error";
+}
+
+void ReportValidationError(ValidationError error, const char* description) {
+ if (g_validation_error_observer) {
+ g_validation_error_observer->set_last_error(error);
+ } else if (description) {
+ MOJO_LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error)
+ << " (" << description << ")";
+ } else {
+ MOJO_LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
+ }
+}
+
+ValidationErrorObserverForTesting::ValidationErrorObserverForTesting()
+ : last_error_(VALIDATION_ERROR_NONE) {
+ MOJO_DCHECK(!g_validation_error_observer);
+ g_validation_error_observer = this;
+}
+
+ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() {
+ MOJO_DCHECK(g_validation_error_observer == this);
+ g_validation_error_observer = nullptr;
+}
+
+bool ReportSerializationWarning(ValidationError error) {
+ if (g_serialization_warning_observer) {
+ g_serialization_warning_observer->set_last_warning(error);
+ return true;
+ }
+
+ return false;
+}
+
+SerializationWarningObserverForTesting::SerializationWarningObserverForTesting()
+ : last_warning_(VALIDATION_ERROR_NONE) {
+ MOJO_DCHECK(!g_serialization_warning_observer);
+ g_serialization_warning_observer = this;
+}
+
+SerializationWarningObserverForTesting::
+ ~SerializationWarningObserverForTesting() {
+ MOJO_DCHECK(g_serialization_warning_observer == this);
+ g_serialization_warning_observer = nullptr;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h
new file mode 100644
index 0000000..1d15bc9
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -0,0 +1,122 @@
+// Copyright 2014 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_VALIDATION_ERRORS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+enum ValidationError {
+ // There is no validation error.
+ VALIDATION_ERROR_NONE,
+ // An object (struct or array) is not 8-byte aligned.
+ VALIDATION_ERROR_MISALIGNED_OBJECT,
+ // An object is not contained inside the message data, or it overlaps other
+ // objects.
+ VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE,
+ // A struct header doesn't make sense, for example:
+ // - |num_bytes| is smaller than the size of the struct header.
+ // - |num_bytes| and |version| don't match.
+ // TODO(yzshen): Consider splitting it into two different error codes. Because
+ // the former indicates someone is misbehaving badly whereas the latter could
+ // be due to an inappropriately-modified .mojom file.
+ VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER,
+ // An array header doesn't make sense, for example:
+ // - |num_bytes| is smaller than the size of the header plus the size required
+ // to store |num_elements| elements.
+ // - For fixed-size arrays, |num_elements| is different than the specified
+ // size.
+ VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ // An encoded handle is illegal.
+ VALIDATION_ERROR_ILLEGAL_HANDLE,
+ // A non-nullable handle field is set to invalid handle.
+ VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ // An encoded pointer is illegal.
+ VALIDATION_ERROR_ILLEGAL_POINTER,
+ // A non-nullable pointer field is set to null.
+ VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ // |flags| in the message header is invalid. The flags are either
+ // inconsistent with one another, inconsistent with other parts of the
+ // message, or unexpected for the message receiver. For example the
+ // receiver is expecting a request message but the flags indicate that
+ // the message is a response message.
+ VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS,
+ // |flags| in the message header indicates that a request ID is required but
+ // there isn't one.
+ VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID,
+ // The |name| field in a message header contains an unexpected value.
+ VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD,
+ // Two parallel arrays which are supposed to represent a map have different
+ // lengths.
+ VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP,
+ // Attempted to deserialize a tagged union with an unknown tag.
+ VALIDATION_ERROR_UNKNOWN_UNION_TAG
+};
+
+const char* ValidationErrorToString(ValidationError error);
+
+void ReportValidationError(ValidationError error,
+ const char* description = nullptr);
+
+// Only used by validation tests and when there is only one thread doing message
+// validation.
+class ValidationErrorObserverForTesting {
+ public:
+ ValidationErrorObserverForTesting();
+ ~ValidationErrorObserverForTesting();
+
+ ValidationError last_error() const { return last_error_; }
+ void set_last_error(ValidationError error) { last_error_ = error; }
+
+ private:
+ ValidationError last_error_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ValidationErrorObserverForTesting);
+};
+
+// Used only by MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING. Don't use it directly.
+//
+// The function returns true if the error is recorded (by a
+// SerializationWarningObserverForTesting object), false otherwise.
+bool ReportSerializationWarning(ValidationError error);
+
+// Only used by serialization tests and when there is only one thread doing
+// message serialization.
+class SerializationWarningObserverForTesting {
+ public:
+ SerializationWarningObserverForTesting();
+ ~SerializationWarningObserverForTesting();
+
+ ValidationError last_warning() const { return last_warning_; }
+ void set_last_warning(ValidationError error) { last_warning_ = error; }
+
+ private:
+ ValidationError last_warning_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(SerializationWarningObserverForTesting);
+};
+
+} // namespace internal
+} // namespace mojo
+
+// In debug build, logs a serialization warning if |condition| evaluates to
+// true:
+// - if there is a SerializationWarningObserverForTesting object alive,
+// records |error| in it;
+// - otherwise, logs a fatal-level message.
+// |error| is the validation error that will be triggered by the receiver
+// of the serialzation result.
+//
+// In non-debug build, does nothing (not even compiling |condition|).
+#define MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( \
+ condition, error, description) \
+ MOJO_DLOG_IF(FATAL, (condition) && !ReportSerializationWarning(error)) \
+ << "The outgoing message will trigger " \
+ << ValidationErrorToString(error) << " at the receiving side (" \
+ << description << ").";
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc
new file mode 100644
index 0000000..2268a15
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_util.cc
@@ -0,0 +1,103 @@
+// Copyright 2015 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/lib/validation_util.h"
+
+#include <limits>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+bool ValidateEncodedPointer(const uint64_t* offset) {
+ // - Make sure |*offset| is no more than 32-bits.
+ // - Cast |offset| to uintptr_t so overflow behavior is well defined across
+ // 32-bit and 64-bit systems.
+ return *offset <= std::numeric_limits<uint32_t>::max() &&
+ (reinterpret_cast<uintptr_t>(offset) +
+ static_cast<uint32_t>(*offset) >=
+ reinterpret_cast<uintptr_t>(offset));
+}
+
+bool ValidateStructHeaderAndClaimMemory(const void* data,
+ BoundsChecker* bounds_checker) {
+ if (!IsAligned(data)) {
+ ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
+ return false;
+ }
+ if (!bounds_checker->IsValidRange(data, sizeof(StructHeader))) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+
+ const StructHeader* header = static_cast<const StructHeader*>(data);
+
+ if (header->num_bytes < sizeof(StructHeader)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+
+ if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateMessageIsRequestWithoutResponse(const Message* message) {
+ if (message->has_flag(kMessageIsResponse) ||
+ message->has_flag(kMessageExpectsResponse)) {
+ ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateMessageIsRequestExpectingResponse(const Message* message) {
+ if (message->has_flag(kMessageIsResponse) ||
+ !message->has_flag(kMessageExpectsResponse)) {
+ ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateMessageIsResponse(const Message* message) {
+ if (message->has_flag(kMessageExpectsResponse) ||
+ !message->has_flag(kMessageIsResponse)) {
+ ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
+ return false;
+ }
+ return true;
+}
+
+bool ValidateControlRequest(const Message* message) {
+ switch (message->header()->name) {
+ case kRunMessageId:
+ return ValidateMessageIsRequestExpectingResponse(message) &&
+ ValidateMessagePayload<RunMessageParams_Data>(message);
+ case kRunOrClosePipeMessageId:
+ return ValidateMessageIsRequestWithoutResponse(message) &&
+ ValidateMessagePayload<RunOrClosePipeMessageParams_Data>(message);
+ }
+ return false;
+}
+
+bool ValidateControlResponse(const Message* message) {
+ if (!ValidateMessageIsResponse(message))
+ return false;
+ switch (message->header()->name) {
+ case kRunMessageId:
+ return ValidateMessagePayload<RunResponseMessageParams_Data>(message);
+ }
+ return false;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h
new file mode 100644
index 0000000..6853f45
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_util.h
@@ -0,0 +1,53 @@
+// Copyright 2015 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_VALIDATION_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+// Checks whether decoding the pointer will overflow and produce a pointer
+// smaller than |offset|.
+bool ValidateEncodedPointer(const uint64_t* offset);
+
+// Validates that |data| contains a valid struct header, in terms of alignment
+// and size (i.e., the |num_bytes| field of the header is sufficient for storing
+// the header itself). Besides, it checks that the memory range
+// [data, data + num_bytes) is not marked as occupied by other objects in
+// |bounds_checker|. On success, the memory range is marked as occupied.
+// Note: Does not verify |version| or that |num_bytes| is correct for the
+// claimed version.
+bool ValidateStructHeaderAndClaimMemory(const void* data,
+ BoundsChecker* bounds_checker);
+
+// Validates that the message is a request which doesn't expect a response.
+bool ValidateMessageIsRequestWithoutResponse(const Message* message);
+// Validates that the message is a request expecting a response.
+bool ValidateMessageIsRequestExpectingResponse(const Message* message);
+// Validates that the message is a response.
+bool ValidateMessageIsResponse(const Message* message);
+
+// Validates that the message payload is a valid struct of type ParamsType.
+template <typename ParamsType>
+bool ValidateMessagePayload(const Message* message) {
+ BoundsChecker bounds_checker(message->payload(), message->payload_num_bytes(),
+ message->handles()->size());
+ return ParamsType::Validate(message->payload(), &bounds_checker);
+}
+
+// The following methods validate control messages defined in
+// interface_control_messages.mojom.
+bool ValidateControlRequest(const Message* message);
+bool ValidateControlResponse(const Message* message);
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/value_traits.h b/mojo/public/cpp/bindings/lib/value_traits.h
new file mode 100644
index 0000000..d3b295b
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/value_traits.h
@@ -0,0 +1,108 @@
+// Copyright 2015 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_VALUE_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALUE_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+
+template <typename T>
+class Array;
+
+template <typename T>
+class AssociatedInterfacePtrInfo;
+
+template <typename T>
+class AssociatedInterfaceRequest;
+
+template <typename T>
+class InlinedStructPtr;
+
+template <typename T>
+class InterfacePtr;
+
+template <typename T>
+class InterfaceRequest;
+
+template <typename K, typename V>
+class Map;
+
+template <typename T>
+class ScopedHandleBase;
+
+template <typename T>
+class StructPtr;
+
+namespace internal {
+
+template <typename T, typename Enable = void>
+struct ValueTraits {
+ static bool Equals(const T& a, const T& b) { return a == b; }
+};
+
+template <typename T>
+struct ValueTraits<
+ T,
+ typename EnableIf<IsSpecializationOf<Array, T>::value ||
+ IsSpecializationOf<Map, T>::value ||
+ IsSpecializationOf<StructPtr, T>::value ||
+ IsSpecializationOf<InlinedStructPtr, T>::value>::type> {
+ static bool Equals(const T& a, const T& b) { return a.Equals(b); }
+};
+
+template <typename T>
+struct ValueTraits<ScopedHandleBase<T>> {
+ static bool Equals(const ScopedHandleBase<T>& a,
+ const ScopedHandleBase<T>& b) {
+ return a.get().value() == b.get().value();
+ }
+};
+
+template <typename T>
+struct ValueTraits<
+ T,
+ typename EnableIf<
+ IsSpecializationOf<InterfaceRequest, T>::value ||
+ IsSpecializationOf<AssociatedInterfaceRequest, T>::value>::type> {
+ static bool Equals(const T& a, const T& b) {
+ if (&a == &b)
+ return true;
+
+ // Now that |a| and |b| refer to different objects, they are equivalent if
+ // and only if they are both invalid.
+ return !a.is_pending() && !b.is_pending();
+ }
+};
+
+template <typename T>
+struct ValueTraits<InterfacePtr<T>> {
+ static bool Equals(const InterfacePtr<T>& a, const InterfacePtr<T>& b) {
+ if (&a == &b)
+ return true;
+
+ // Now that |a| and |b| refer to different objects, they are equivalent if
+ // and only if they are both null.
+ return !a && !b;
+ }
+};
+
+template <typename T>
+struct ValueTraits<AssociatedInterfacePtrInfo<T>> {
+ static bool Equals(const AssociatedInterfacePtrInfo<T>& a,
+ const AssociatedInterfacePtrInfo<T>& b) {
+ if (&a == &b)
+ return true;
+
+ // Now that |a| and |b| refer to different objects, they are equivalent if
+ // and only if they are both invalid.
+ return !a.is_valid() && !b.is_valid();
+ }
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALUE_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h
new file mode 100644
index 0000000..0f08d83
--- /dev/null
+++ b/mojo/public/cpp/bindings/map.h
@@ -0,0 +1,296 @@
+// Copyright 2014 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_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
+
+#include <map>
+
+#include "mojo/public/cpp/bindings/lib/map_internal.h"
+#include "mojo/public/cpp/bindings/lib/value_traits.h"
+
+namespace mojo {
+
+// A move-only map that can handle move-only values. Map has the following
+// characteristics:
+// - The map itself can be null, and this is distinct from empty.
+// - Keys must not be move-only.
+// - The Key-type's "<" operator is used to sort the entries, and also is
+// used to determine equality of the key values.
+// - There can only be one entry per unique key.
+// - Values of move-only types will be moved into the Map when they are added
+// using the insert() method.
+template <typename Key, typename Value>
+class Map {
+ MOJO_MOVE_ONLY_TYPE(Map)
+
+ public:
+ // Map keys cannot be move only classes.
+ static_assert(!internal::IsMoveOnlyType<Key>::value,
+ "Map keys cannot be move only types.");
+
+ typedef internal::MapTraits<Key,
+ Value,
+ internal::IsMoveOnlyType<Value>::value> Traits;
+ typedef typename Traits::KeyStorageType KeyStorageType;
+ typedef typename Traits::KeyRefType KeyRefType;
+ typedef typename Traits::KeyConstRefType KeyConstRefType;
+ typedef typename Traits::KeyForwardType KeyForwardType;
+
+ typedef typename Traits::ValueStorageType ValueStorageType;
+ typedef typename Traits::ValueRefType ValueRefType;
+ typedef typename Traits::ValueConstRefType ValueConstRefType;
+ typedef typename Traits::ValueForwardType ValueForwardType;
+
+ typedef internal::Map_Data<typename internal::WrapperTraits<Key>::DataType,
+ typename internal::WrapperTraits<Value>::DataType>
+ Data_;
+
+ Map() : is_null_(true) {}
+
+ // Constructs a non-null Map containing the specified |keys| mapped to the
+ // corresponding |values|.
+ Map(mojo::Array<Key> keys, mojo::Array<Value> values) : is_null_(false) {
+ MOJO_DCHECK(keys.size() == values.size());
+ Traits::InitializeFrom(&map_, keys.Pass(), values.Pass());
+ }
+
+ ~Map() { Traits::Finalize(&map_); }
+
+ Map(Map&& other) : is_null_(true) { Take(&other); }
+ Map& operator=(Map&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ // Copies the contents of some other type of map into a new Map using a
+ // TypeConverter. A TypeConverter for std::map to Map is defined below.
+ template <typename U>
+ static Map From(const U& other) {
+ return TypeConverter<Map, U>::Convert(other);
+ }
+
+ // Copies the contents of the Map into some other type of map. A TypeConverter
+ // for Map to std::map is defined below.
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, Map>::Convert(*this);
+ }
+
+ // Destroys the contents of the Map and leaves it in the null state.
+ void reset() {
+ if (!map_.empty()) {
+ Traits::Finalize(&map_);
+ map_.clear();
+ }
+ is_null_ = true;
+ }
+
+ bool is_null() const { return is_null_; }
+
+ // Indicates the number of keys in the map.
+ size_t size() const { return map_.size(); }
+
+ void mark_non_null() { is_null_ = false; }
+
+ // Inserts a key-value pair into the map, moving the value by calling its
+ // Pass() method if it is a move-only type. Like std::map, this does not
+ // insert |value| if |key| is already a member of the map.
+ void insert(KeyForwardType key, ValueForwardType value) {
+ is_null_ = false;
+ Traits::Insert(&map_, key, value);
+ }
+
+ // Returns a reference to the value associated with the specified key,
+ // crashing the process if the key is not present in the map.
+ ValueRefType at(KeyForwardType key) { return Traits::at(&map_, key); }
+ ValueConstRefType at(KeyForwardType key) const {
+ return Traits::at(&map_, key);
+ }
+
+ // 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).
+ ValueRefType operator[](KeyForwardType key) {
+ is_null_ = false;
+ return Traits::GetOrInsert(&map_, key);
+ }
+
+ // Swaps the contents of this Map with another Map of the same type (including
+ // nullness).
+ void Swap(Map<Key, Value>* other) {
+ std::swap(is_null_, other->is_null_);
+ map_.swap(other->map_);
+ }
+
+ // Swaps the contents of this Map with an std::map containing keys and values
+ // of the same type. Since std::map cannot represent the null state, the
+ // std::map will be empty if Map is null. The Map will always be left in a
+ // non-null state.
+ void Swap(std::map<Key, Value>* other) {
+ is_null_ = false;
+ map_.swap(*other);
+ }
+
+ // Removes all contents from the Map and places them into parallel key/value
+ // arrays. Each key will be copied from the source to the destination, and
+ // values will be copied unless their type is designated move-only, in which
+ // case they will be passed by calling their Pass() method. Either way, the
+ // Map will be left in a null state.
+ void DecomposeMapTo(mojo::Array<Key>* keys, mojo::Array<Value>* values) {
+ Traits::Decompose(&map_, keys, values);
+ Traits::Finalize(&map_);
+ map_.clear();
+ is_null_ = true;
+ }
+
+ // Returns a new Map that contains a copy of the contents of this map. If the
+ // values are of a type that is designated move-only, they will be cloned
+ // using the Clone() method of the type. Please note that calling this method
+ // will fail compilation if the value type cannot be cloned (which usually
+ // means that it is a Mojo handle type or a type that contains Mojo handles).
+ Map Clone() const {
+ Map result;
+ result.is_null_ = is_null_;
+ Traits::Clone(map_, &result.map_);
+ return result.Pass();
+ }
+
+ // Indicates whether the contents of this map are equal to those of another
+ // Map (including nullness). Keys are compared by the != operator. Values are
+ // compared as follows:
+ // - Map, Array, Struct, or StructPtr values are compared by their Equals()
+ // method.
+ // - ScopedHandleBase-derived types are compared by their handles.
+ // - Values of other types are compared by their "==" operator.
+ bool Equals(const Map& other) const {
+ if (is_null() != other.is_null())
+ return false;
+ if (size() != other.size())
+ return false;
+ auto i = begin();
+ auto j = other.begin();
+ while (i != end()) {
+ if (i.GetKey() != j.GetKey())
+ return false;
+ if (!internal::ValueTraits<Value>::Equals(i.GetValue(), j.GetValue()))
+ return false;
+ ++i;
+ ++j;
+ }
+ return true;
+ }
+
+ // A read-only iterator for Map.
+ class ConstMapIterator {
+ public:
+ ConstMapIterator(
+ const typename std::map<KeyStorageType,
+ ValueStorageType>::const_iterator& it)
+ : it_(it) {}
+
+ // Returns a const reference to the key and value.
+ KeyConstRefType GetKey() { return Traits::GetKey(it_); }
+ ValueConstRefType GetValue() { return Traits::GetValue(it_); }
+
+ ConstMapIterator& operator*() {
+ return *this;
+ }
+ ConstMapIterator& operator++() {
+ it_++;
+ return *this;
+ }
+ bool operator!=(const ConstMapIterator& rhs) const {
+ return it_ != rhs.it_;
+ }
+ bool operator==(const ConstMapIterator& rhs) const {
+ return it_ == rhs.it_;
+ }
+
+ private:
+ typename std::map<KeyStorageType, ValueStorageType>::const_iterator it_;
+ };
+
+ // Provide read-only iteration over map members in a way similar to STL
+ // collections.
+ ConstMapIterator begin() const { return ConstMapIterator(map_.begin()); }
+ ConstMapIterator end() const { return ConstMapIterator(map_.end()); }
+
+ // Returns the iterator pointing to the entry for |key|, if present, or else
+ // returns end().
+ ConstMapIterator find(KeyForwardType key) const {
+ return ConstMapIterator(map_.find(key));
+ }
+
+ private:
+ typedef std::map<KeyStorageType, ValueStorageType> Map::*Testable;
+
+ public:
+ // The Map may be used in boolean expressions to determine if it is non-null,
+ // but is not implicitly convertible to an actual bool value (which would be
+ // dangerous).
+ operator Testable() const { return is_null_ ? 0 : &Map::map_; }
+
+ private:
+ // Forbid the == and != operators explicitly, otherwise Map will be converted
+ // to Testable to do == or != comparison.
+ template <typename T, typename U>
+ bool operator==(const Map<T, U>& other) const = delete;
+ template <typename T, typename U>
+ bool operator!=(const Map<T, U>& other) const = delete;
+
+ void Take(Map* other) {
+ reset();
+ Swap(other);
+ }
+
+ std::map<KeyStorageType, ValueStorageType> map_;
+ bool is_null_;
+};
+
+// Copies the contents of an std::map to a new Map, optionally changing the
+// types of the keys and values along the way using TypeConverter.
+template <typename MojoKey,
+ typename MojoValue,
+ typename STLKey,
+ typename STLValue>
+struct TypeConverter<Map<MojoKey, MojoValue>, std::map<STLKey, STLValue>> {
+ static Map<MojoKey, MojoValue> Convert(
+ const std::map<STLKey, STLValue>& input) {
+ Map<MojoKey, MojoValue> result;
+ result.mark_non_null();
+ for (auto& pair : input) {
+ result.insert(TypeConverter<MojoKey, STLKey>::Convert(pair.first),
+ TypeConverter<MojoValue, STLValue>::Convert(pair.second));
+ }
+ return result.Pass();
+ }
+};
+
+// Copies the contents of a Map to an std::map, optionally changing the types of
+// the keys and values along the way using TypeConverter.
+template <typename MojoKey,
+ typename MojoValue,
+ typename STLKey,
+ typename STLValue>
+struct TypeConverter<std::map<STLKey, STLValue>, Map<MojoKey, MojoValue>> {
+ static std::map<STLKey, STLValue> Convert(
+ const Map<MojoKey, MojoValue>& input) {
+ std::map<STLKey, STLValue> result;
+ if (!input.is_null()) {
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ result.insert(std::make_pair(
+ TypeConverter<STLKey, MojoKey>::Convert(it.GetKey()),
+ TypeConverter<STLValue, MojoValue>::Convert(it.GetValue())));
+ }
+ }
+ return result;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
new file mode 100644
index 0000000..7e34606
--- /dev/null
+++ b/mojo/public/cpp/bindings/message.h
@@ -0,0 +1,159 @@
+// Copyright 2014 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_MESSAGE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+// Message is a holder for the data and handles to be sent over a MessagePipe.
+// Message owns its data and handles, but a consumer of Message is free to
+// mutate the data and handles. The message's data is comprised of a header
+// followed by payload.
+class Message {
+ public:
+ Message();
+ ~Message();
+
+ void Reset();
+
+ void AllocData(uint32_t num_bytes);
+ void AllocUninitializedData(uint32_t num_bytes);
+
+ // Transfers data and handles to |destination|.
+ void MoveTo(Message* destination);
+
+ uint32_t data_num_bytes() const { return data_num_bytes_; }
+
+ // Access the raw bytes of the message.
+ const uint8_t* data() const {
+ return reinterpret_cast<const uint8_t*>(data_);
+ }
+ uint8_t* mutable_data() { return reinterpret_cast<uint8_t*>(data_); }
+
+ // Access the header.
+ const internal::MessageHeader* header() const { return &data_->header; }
+
+ uint32_t name() const { return data_->header.name; }
+ bool has_flag(uint32_t flag) const { return !!(data_->header.flags & flag); }
+
+ // Access the request_id field (if present).
+ bool has_request_id() const { return data_->header.version >= 1; }
+ uint64_t request_id() const {
+ MOJO_DCHECK(has_request_id());
+ return static_cast<const internal::MessageHeaderWithRequestID*>(
+ &data_->header)->request_id;
+ }
+ void set_request_id(uint64_t request_id) {
+ MOJO_DCHECK(has_request_id());
+ static_cast<internal::MessageHeaderWithRequestID*>(&data_->header)
+ ->request_id = request_id;
+ }
+
+ // Access the payload.
+ const uint8_t* payload() const {
+ return reinterpret_cast<const uint8_t*>(data_) + data_->header.num_bytes;
+ }
+ uint8_t* mutable_payload() {
+ return reinterpret_cast<uint8_t*>(data_) + data_->header.num_bytes;
+ }
+ uint32_t payload_num_bytes() const {
+ MOJO_DCHECK(data_num_bytes_ >= data_->header.num_bytes);
+ return data_num_bytes_ - data_->header.num_bytes;
+ }
+
+ // Access the handles.
+ const std::vector<Handle>* handles() const { return &handles_; }
+ std::vector<Handle>* mutable_handles() { return &handles_; }
+
+ private:
+ void Initialize();
+ void FreeDataAndCloseHandles();
+
+ uint32_t data_num_bytes_;
+ internal::MessageData* data_;
+ std::vector<Handle> handles_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Message);
+};
+
+class MessageReceiver {
+ public:
+ virtual ~MessageReceiver() {}
+
+ // The receiver may mutate the given message. Returns true if the message
+ // was accepted and false otherwise, indicating that the message was invalid
+ // or malformed.
+ virtual bool Accept(Message* message) MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+class MessageReceiverWithResponder : public MessageReceiver {
+ public:
+ ~MessageReceiverWithResponder() override {}
+
+ // A variant on Accept that registers a MessageReceiver (known as the
+ // responder) to handle the response message generated from the given
+ // message. The responder's Accept method may be called during
+ // AcceptWithResponder or some time after its return.
+ //
+ // NOTE: Upon returning true, AcceptWithResponder assumes ownership of
+ // |responder| and will delete it after calling |responder->Accept| or upon
+ // its own destruction.
+ //
+ virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder)
+ MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+// A MessageReceiver that is also able to provide status about the state
+// of the underlying MessagePipe to which it will be forwarding messages
+// received via the |Accept()| call.
+class MessageReceiverWithStatus : public MessageReceiver {
+ public:
+ ~MessageReceiverWithStatus() override {}
+
+ // Returns |true| if this MessageReceiver is currently bound to a MessagePipe,
+ // the pipe has not been closed, and the pipe has not encountered an error.
+ virtual bool IsValid() = 0;
+};
+
+// An alternative to MessageReceiverWithResponder for cases in which it
+// is necessary for the implementor of this interface to know about the status
+// of the MessagePipe which will carry the responses.
+class MessageReceiverWithResponderStatus : public MessageReceiver {
+ public:
+ ~MessageReceiverWithResponderStatus() override {}
+
+ // A variant on Accept that registers a MessageReceiverWithStatus (known as
+ // the responder) to handle the response message generated from the given
+ // message. Any of the responder's methods (Accept or IsValid) may be called
+ // during AcceptWithResponder or some time after its return.
+ //
+ // NOTE: Upon returning true, AcceptWithResponder assumes ownership of
+ // |responder| and will delete it after calling |responder->Accept| or upon
+ // its own destruction.
+ //
+ virtual bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder)
+ MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+// Read a single message from the pipe and dispatch to the given receiver. The
+// receiver may be null, in which case the message is simply discarded.
+// Returns MOJO_RESULT_SHOULD_WAIT if the caller should wait on the handle to
+// become readable. Returns MOJO_RESULT_OK if a message was dispatched and
+// otherwise returns an error code if something went wrong.
+//
+// NOTE: The message hasn't been validated and may be malformed!
+MojoResult ReadAndDispatchMessage(MessagePipeHandle handle,
+ MessageReceiver* receiver,
+ bool* receiver_result);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
diff --git a/mojo/public/cpp/bindings/message_filter.h b/mojo/public/cpp/bindings/message_filter.h
new file mode 100644
index 0000000..8e10f6b
--- /dev/null
+++ b/mojo/public/cpp/bindings/message_filter.h
@@ -0,0 +1,39 @@
+// Copyright 2014 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_MESSAGE_FILTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// This class is the base class for message filters. Subclasses should
+// implement the pure virtual method Accept() inherited from MessageReceiver to
+// process messages and/or forward them to |sink_|.
+class MessageFilter : public MessageReceiver {
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit MessageFilter(MessageReceiver* sink = nullptr);
+ ~MessageFilter() override;
+
+ void set_sink(MessageReceiver* sink) { sink_ = sink; }
+
+ protected:
+ MessageReceiver* sink_;
+};
+
+// A trivial filter that simply forwards every message it receives to |sink_|.
+class PassThroughFilter : public MessageFilter {
+ public:
+ explicit PassThroughFilter(MessageReceiver* sink = nullptr);
+
+ bool Accept(Message* message) override;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
diff --git a/mojo/public/cpp/bindings/no_interface.h b/mojo/public/cpp/bindings/no_interface.h
new file mode 100644
index 0000000..d8915cd
--- /dev/null
+++ b/mojo/public/cpp/bindings/no_interface.h
@@ -0,0 +1,52 @@
+// Copyright 2014 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_NO_INTERFACE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// NoInterface is for use in cases when a non-existent or empty interface is
+// needed.
+
+class NoInterfaceProxy;
+class NoInterfaceStub;
+
+class NoInterface {
+ public:
+ static const char* Name_;
+ typedef NoInterfaceProxy Proxy_;
+ typedef NoInterfaceStub Stub_;
+ typedef PassThroughFilter RequestValidator_;
+ typedef PassThroughFilter ResponseValidator_;
+ virtual ~NoInterface() {}
+};
+
+class NoInterfaceProxy : public NoInterface {
+ public:
+ explicit NoInterfaceProxy(MessageReceiver* receiver) {}
+};
+
+class NoInterfaceStub : public MessageReceiverWithResponder {
+ public:
+ NoInterfaceStub() {}
+ void set_sink(NoInterface* sink) {}
+ NoInterface* sink() { return nullptr; }
+ bool Accept(Message* message) override;
+ bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override;
+};
+
+// AnyInterface is for use in cases where any interface would do (e.g., see the
+// Shell::Connect method).
+
+typedef NoInterface AnyInterface;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h
new file mode 100644
index 0000000..e0ed4ba
--- /dev/null
+++ b/mojo/public/cpp/bindings/string.h
@@ -0,0 +1,175 @@
+// Copyright 2014 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_STRING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+// A UTF-8 encoded character string that can be null. Provides functions that
+// are similar to std::string, along with access to the underlying std::string
+// object.
+class String {
+ public:
+ typedef internal::String_Data Data_;
+
+ String() : is_null_(true) {}
+ String(const std::string& str) : value_(str), is_null_(false) {}
+ String(const char* chars) : is_null_(!chars) {
+ if (chars)
+ value_ = chars;
+ }
+ String(const char* chars, size_t num_chars)
+ : value_(chars, num_chars), is_null_(false) {}
+ String(const mojo::String& str)
+ : value_(str.value_), is_null_(str.is_null_) {}
+
+ template <size_t N>
+ String(const char chars[N])
+ : value_(chars, N - 1), is_null_(false) {}
+
+ template <typename U>
+ static String From(const U& other) {
+ return TypeConverter<String, U>::Convert(other);
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, String>::Convert(*this);
+ }
+
+ String& operator=(const mojo::String& str) {
+ value_ = str.value_;
+ is_null_ = str.is_null_;
+ return *this;
+ }
+ String& operator=(const std::string& str) {
+ value_ = str;
+ is_null_ = false;
+ return *this;
+ }
+ String& operator=(const char* chars) {
+ is_null_ = !chars;
+ if (chars) {
+ value_ = chars;
+ } else {
+ value_.clear();
+ }
+ return *this;
+ }
+
+ void reset() {
+ value_.clear();
+ is_null_ = true;
+ }
+
+ bool is_null() const { return is_null_; }
+
+ size_t size() const { return value_.size(); }
+
+ const char* data() const { return value_.data(); }
+
+ const char& at(size_t offset) const { return value_.at(offset); }
+ const char& operator[](size_t offset) const { return value_[offset]; }
+
+ const std::string& get() const { return value_; }
+ operator const std::string&() const { return value_; }
+
+ void Swap(String* other) {
+ std::swap(is_null_, other->is_null_);
+ value_.swap(other->value_);
+ }
+
+ void Swap(std::string* other) {
+ is_null_ = false;
+ value_.swap(*other);
+ }
+
+ private:
+ typedef std::string String::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &String::value_; }
+
+ private:
+ std::string value_;
+ bool is_null_;
+};
+
+inline bool operator==(const String& a, const String& b) {
+ return a.is_null() == b.is_null() && a.get() == b.get();
+}
+inline bool operator==(const char* a, const String& b) {
+ return !b.is_null() && a == b.get();
+}
+inline bool operator==(const String& a, const char* b) {
+ return !a.is_null() && a.get() == b;
+}
+inline bool operator!=(const String& a, const String& b) {
+ return !(a == b);
+}
+inline bool operator!=(const char* a, const String& b) {
+ return !(a == b);
+}
+inline bool operator!=(const String& a, const char* b) {
+ return !(a == b);
+}
+
+inline std::ostream& operator<<(std::ostream& out, const String& s) {
+ return out << s.get();
+}
+
+inline bool operator<(const String& a, const String& b) {
+ if (a.is_null())
+ return !b.is_null();
+ if (b.is_null())
+ return false;
+
+ return a.get() < b.get();
+}
+
+// TODO(darin): Add similar variants of operator<,<=,>,>=
+
+template <>
+struct TypeConverter<String, std::string> {
+ static String Convert(const std::string& input) { return String(input); }
+};
+
+template <>
+struct TypeConverter<std::string, String> {
+ static std::string Convert(const String& input) { return input; }
+};
+
+template <size_t N>
+struct TypeConverter<String, char[N]> {
+ static String Convert(const char input[N]) {
+ MOJO_DCHECK(input);
+ return String(input, N - 1);
+ }
+};
+
+// Appease MSVC.
+template <size_t N>
+struct TypeConverter<String, const char[N]> {
+ static String Convert(const char input[N]) {
+ MOJO_DCHECK(input);
+ return String(input, N - 1);
+ }
+};
+
+template <>
+struct TypeConverter<String, const char*> {
+ // |input| may be null, in which case a null String will be returned.
+ static String Convert(const char* input) { return String(input); }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
diff --git a/mojo/public/cpp/bindings/strong_binding.h b/mojo/public/cpp/bindings/strong_binding.h
new file mode 100644
index 0000000..860260e
--- /dev/null
+++ b/mojo/public/cpp/bindings/strong_binding.h
@@ -0,0 +1,127 @@
+// Copyright 2014 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_STRONG_BINDING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// This connects an interface implementation strongly to a pipe. When a
+// connection error is detected the implementation is deleted. Deleting the
+// connector also closes the pipe.
+//
+// Example of an implementation that is always bound strongly to a pipe
+//
+// class StronglyBound : public Foo {
+// public:
+// explicit StronglyBound(InterfaceRequest<Foo> request)
+// : binding_(this, request.Pass()) {}
+//
+// // Foo implementation here
+//
+// private:
+// StrongBinding<Foo> binding_;
+// };
+//
+// class MyFooFactory : public InterfaceFactory<Foo> {
+// public:
+// void Create(..., InterfaceRequest<Foo> request) override {
+// new StronglyBound(request.Pass()); // The binding now owns the
+// // instance of StronglyBound.
+// }
+// };
+template <typename Interface>
+class StrongBinding {
+ MOJO_MOVE_ONLY_TYPE(StrongBinding)
+
+ public:
+ explicit StrongBinding(Interface* impl) : binding_(impl) {
+ binding_.set_connection_error_handler([this]() { OnConnectionError(); });
+ }
+
+ StrongBinding(
+ Interface* impl,
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter())
+ : StrongBinding(impl) {
+ binding_.Bind(handle.Pass(), waiter);
+ }
+
+ StrongBinding(
+ Interface* impl,
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter())
+ : StrongBinding(impl) {
+ binding_.Bind(ptr, waiter);
+ }
+
+ StrongBinding(
+ Interface* impl,
+ InterfaceRequest<Interface> request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter())
+ : StrongBinding(impl) {
+ binding_.Bind(request.Pass(), waiter);
+ }
+
+ ~StrongBinding() {}
+
+ void Bind(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ assert(!binding_.is_bound());
+ binding_.Bind(handle.Pass(), waiter);
+ }
+
+ void Bind(
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ assert(!binding_.is_bound());
+ binding_.Bind(ptr, waiter);
+ }
+
+ void Bind(
+ InterfaceRequest<Interface> request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ assert(!binding_.is_bound());
+ binding_.Bind(request.Pass(), waiter);
+ }
+
+ bool WaitForIncomingMethodCall() {
+ return binding_.WaitForIncomingMethodCall();
+ }
+
+ // Note: The error handler must not delete the interface implementation.
+ void set_connection_error_handler(const Closure& error_handler) {
+ connection_error_handler_ = error_handler;
+ }
+
+ Interface* impl() { return binding_.impl(); }
+ // Exposed for testing, should not generally be used.
+ internal::Router* internal_router() { return binding_.internal_router(); }
+
+ void OnConnectionError() {
+ connection_error_handler_.Run();
+ delete binding_.impl();
+ }
+
+ private:
+ Closure connection_error_handler_;
+ Binding<Interface> binding_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
diff --git a/mojo/public/cpp/bindings/struct_ptr.h b/mojo/public/cpp/bindings/struct_ptr.h
new file mode 100644
index 0000000..b324e93
--- /dev/null
+++ b/mojo/public/cpp/bindings/struct_ptr.h
@@ -0,0 +1,206 @@
+// Copyright 2014 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_STRUCT_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+
+#include <new>
+
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Struct>
+class StructHelper {
+ public:
+ template <typename Ptr>
+ static void Initialize(Ptr* ptr) {
+ ptr->Initialize();
+ }
+};
+
+} // namespace internal
+
+// Smart pointer wrapping a mojom structure with move-only semantics.
+template <typename Struct>
+class StructPtr {
+ MOJO_MOVE_ONLY_TYPE(StructPtr)
+
+ public:
+
+ StructPtr() : ptr_(nullptr) {}
+ StructPtr(decltype(nullptr)) : ptr_(nullptr) {}
+
+ ~StructPtr() { delete ptr_; }
+
+ StructPtr& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); }
+ StructPtr& operator=(StructPtr&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, StructPtr>::Convert(*this);
+ }
+
+ void reset() {
+ if (ptr_) {
+ delete ptr_;
+ ptr_ = nullptr;
+ }
+ }
+
+ bool is_null() const { return ptr_ == nullptr; }
+
+ Struct& operator*() const {
+ MOJO_DCHECK(ptr_);
+ return *ptr_;
+ }
+ Struct* operator->() const {
+ MOJO_DCHECK(ptr_);
+ return ptr_;
+ }
+ Struct* get() const { return ptr_; }
+
+ void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); }
+
+ // Please note that calling this method will fail compilation if the value
+ // type |Struct| doesn't have a Clone() method defined (which usually means
+ // that it contains Mojo handles).
+ StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); }
+
+ bool Equals(const StructPtr& other) const {
+ if (is_null() || other.is_null())
+ return is_null() && other.is_null();
+ return ptr_->Equals(*other.ptr_);
+ }
+
+ private:
+ typedef Struct* StructPtr::*Testable;
+
+ public:
+ operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
+
+ private:
+ friend class internal::StructHelper<Struct>;
+
+ // Forbid the == and != operators explicitly, otherwise StructPtr will be
+ // converted to Testable to do == or != comparison.
+ template <typename T>
+ bool operator==(const StructPtr<T>& other) const = delete;
+ template <typename T>
+ bool operator!=(const StructPtr<T>& other) const = delete;
+
+ void Initialize() {
+ MOJO_DCHECK(!ptr_);
+ ptr_ = new Struct();
+ }
+
+ void Take(StructPtr* other) {
+ reset();
+ Swap(other);
+ }
+
+ Struct* ptr_;
+};
+
+// Designed to be used when Struct is small and copyable.
+template <typename Struct>
+class InlinedStructPtr {
+ MOJO_MOVE_ONLY_TYPE(InlinedStructPtr);
+
+ public:
+
+ InlinedStructPtr() : is_null_(true) {}
+ InlinedStructPtr(decltype(nullptr)) : is_null_(true) {}
+
+ ~InlinedStructPtr() {}
+
+ InlinedStructPtr& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); }
+ InlinedStructPtr& operator=(InlinedStructPtr&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, InlinedStructPtr>::Convert(*this);
+ }
+
+ void reset() {
+ is_null_ = true;
+ value_. ~Struct();
+ new (&value_) Struct();
+ }
+
+ bool is_null() const { return is_null_; }
+
+ Struct& operator*() const {
+ MOJO_DCHECK(!is_null_);
+ return value_;
+ }
+ Struct* operator->() const {
+ MOJO_DCHECK(!is_null_);
+ return &value_;
+ }
+ Struct* get() const { return &value_; }
+
+ void Swap(InlinedStructPtr* other) {
+ std::swap(value_, other->value_);
+ std::swap(is_null_, other->is_null_);
+ }
+
+ InlinedStructPtr Clone() const {
+ return is_null() ? InlinedStructPtr() : value_.Clone();
+ }
+ bool Equals(const InlinedStructPtr& other) const {
+ if (is_null() || other.is_null())
+ return is_null() && other.is_null();
+ return value_.Equals(other.value_);
+ }
+
+ private:
+ typedef Struct InlinedStructPtr::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
+
+ private:
+ friend class internal::StructHelper<Struct>;
+
+ // Forbid the == and != operators explicitly, otherwise InlinedStructPtr will
+ // be converted to Testable to do == or != comparison.
+ template <typename T>
+ bool operator==(const InlinedStructPtr<T>& other) const = delete;
+ template <typename T>
+ bool operator!=(const InlinedStructPtr<T>& other) const = delete;
+
+ void Initialize() { is_null_ = false; }
+
+ void Take(InlinedStructPtr* other) {
+ reset();
+ Swap(other);
+ }
+
+ mutable Struct value_;
+ bool is_null_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
new file mode 100644
index 0000000..1bc018c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -0,0 +1,86 @@
+# Copyright 2014 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.
+
+import("../../../mojo_application.gni")
+import("../../../mojo_sdk.gni")
+
+mojo_sdk_source_set("tests") {
+ testonly = true
+
+ sources = [
+ "array_unittest.cc",
+ "binding_callback_unittest.cc",
+ "binding_unittest.cc",
+ "bounds_checker_unittest.cc",
+ "buffer_unittest.cc",
+ "callback_unittest.cc",
+ "connector_unittest.cc",
+ "constant_unittest.cc",
+ "container_test_util.cc",
+ "equals_unittest.cc",
+ "handle_passing_unittest.cc",
+ "interface_ptr_unittest.cc",
+ "map_unittest.cc",
+ "message_queue.cc",
+ "message_queue.h",
+ "request_response_unittest.cc",
+ "router_unittest.cc",
+ "sample_service_unittest.cc",
+ "serialization_warning_unittest.cc",
+ "string_unittest.cc",
+ "struct_unittest.cc",
+ "type_conversion_unittest.cc",
+ "union_unittest.cc",
+ "validation_unittest.cc",
+ ]
+
+ deps = [
+ ":mojo_public_bindings_test_utils",
+ "//mojo/environment:chromium",
+ "//mojo/message_pump",
+ "//testing/gtest",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/cpp/bindings",
+ "mojo/public/cpp/bindings:callback",
+ "mojo/public/cpp/system",
+ "mojo/public/cpp/test_support:test_utils",
+ "mojo/public/cpp/utility",
+ "mojo/public/interfaces/bindings/tests:test_associated_interfaces",
+ "mojo/public/interfaces/bindings/tests:test_interfaces",
+ "mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
+ ]
+}
+
+mojo_sdk_source_set("perftests") {
+ testonly = true
+
+ sources = [
+ "bindings_perftest.cc",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/cpp/bindings",
+ "mojo/public/cpp/bindings:callback",
+ "mojo/public/cpp/environment:standalone",
+ "mojo/public/cpp/system",
+ "mojo/public/cpp/test_support:test_utils",
+ "mojo/public/cpp/utility",
+ "mojo/public/interfaces/bindings/tests:test_interfaces",
+ ]
+}
+
+mojo_sdk_source_set("mojo_public_bindings_test_utils") {
+ sources = [
+ "validation_test_input_parser.cc",
+ "validation_test_input_parser.h",
+ ]
+
+ mojo_sdk_deps = [ "mojo/public/c/system" ]
+}
diff --git a/mojo/public/cpp/bindings/tests/array_unittest.cc b/mojo/public/cpp/bindings/tests/array_unittest.cc
new file mode 100644
index 0000000..8befe81
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/array_unittest.cc
@@ -0,0 +1,438 @@
+// Copyright 2014 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/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.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 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] = pipe.handle0.Pass();
+ handles[1].reset(pipe.handle1.release());
+
+ EXPECT_FALSE(pipe.handle0.is_valid());
+ EXPECT_FALSE(pipe.handle1.is_valid());
+
+ Array<ScopedMessagePipeHandle> handles2 = handles.Pass();
+ EXPECT_TRUE(handles2[0].is_valid());
+ EXPECT_TRUE(handles2[1].is_valid());
+
+ ScopedMessagePipeHandle pipe_handle = handles2[0].Pass();
+ 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] = pipe.handle0.Pass();
+ 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[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);
+ EXPECT_EQ(8U + 4 * 4U, size);
+
+ FixedBufferForTesting buf(size);
+ Array_Data<int32_t>* data;
+ ArrayValidateParams validate_params(0, false, nullptr);
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<int32_t> array2;
+ Deserialize_(data, &array2);
+
+ 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(0);
+ size_t size = GetSerializedSize_(array);
+ EXPECT_EQ(8U, size);
+
+ FixedBufferForTesting buf(size);
+ Array_Data<int32_t>* data;
+ ArrayValidateParams validate_params(0, false, nullptr);
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<int32_t> array2;
+ Deserialize_(data, &array2);
+ 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] = inner.Pass();
+ }
+
+ size_t size = GetSerializedSize_(array);
+ 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_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<Array<int32_t>> array2;
+ Deserialize_(data, &array2);
+
+ 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);
+ EXPECT_EQ(8U + 8U, size);
+
+ FixedBufferForTesting buf(size);
+ Array_Data<bool>* data;
+ ArrayValidateParams validate_params(0, false, nullptr);
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<bool> array2;
+ Deserialize_(data, &array2);
+
+ 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);
+ 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_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<String> array2;
+ Deserialize_(data, &array2);
+
+ 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.reset();
+ 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.reset();
+ EXPECT_EQ(0u, MoveOnlyType::num_instances());
+ EXPECT_FALSE(array);
+ array.resize(0);
+ EXPECT_EQ(0u, MoveOnlyType::num_instances());
+ EXPECT_TRUE(array);
+}
+
+TEST_F(ArrayTest, PushBack_Copyable) {
+ ASSERT_EQ(0u, CopyableType::num_instances());
+ mojo::Array<CopyableType> array(2);
+ array.reset();
+ std::vector<CopyableType*> value_ptrs;
+ size_t capacity = array.storage().capacity();
+ for (size_t i = 0; i < capacity; i++) {
+ CopyableType value;
+ value_ptrs.push_back(value.ptr());
+ array.push_back(value);
+ ASSERT_EQ(i + 1, array.size());
+ ASSERT_EQ(i + 1, value_ptrs.size());
+ EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
+ EXPECT_TRUE(array[i].copied());
+ EXPECT_EQ(value_ptrs[i], array[i].ptr());
+ array[i].ResetCopied();
+ EXPECT_TRUE(array);
+ }
+ {
+ CopyableType value;
+ value_ptrs.push_back(value.ptr());
+ array.push_back(value);
+ EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
+ }
+ ASSERT_EQ(capacity + 1, array.size());
+ EXPECT_EQ(array.size(), CopyableType::num_instances());
+
+ for (size_t i = 0; i < array.size(); i++) {
+ EXPECT_TRUE(array[i].copied());
+ EXPECT_EQ(value_ptrs[i], array[i].ptr());
+ }
+ array.reset();
+ EXPECT_EQ(0u, CopyableType::num_instances());
+}
+
+TEST_F(ArrayTest, PushBack_MoveOnly) {
+ ASSERT_EQ(0u, MoveOnlyType::num_instances());
+ mojo::Array<MoveOnlyType> array(2);
+ array.reset();
+ std::vector<MoveOnlyType*> value_ptrs;
+ size_t capacity = array.storage().capacity();
+ for (size_t i = 0; i < capacity; i++) {
+ MoveOnlyType value;
+ value_ptrs.push_back(value.ptr());
+ array.push_back(value.Pass());
+ ASSERT_EQ(i + 1, array.size());
+ ASSERT_EQ(i + 1, value_ptrs.size());
+ EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
+ EXPECT_TRUE(array[i].moved());
+ EXPECT_EQ(value_ptrs[i], array[i].ptr());
+ array[i].ResetMoved();
+ EXPECT_TRUE(array);
+ }
+ {
+ MoveOnlyType value;
+ value_ptrs.push_back(value.ptr());
+ array.push_back(value.Pass());
+ EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
+ }
+ ASSERT_EQ(capacity + 1, array.size());
+ EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
+
+ for (size_t i = 0; i < array.size(); i++) {
+ EXPECT_TRUE(array[i].moved());
+ EXPECT_EQ(value_ptrs[i], array[i].ptr());
+ }
+ array.reset();
+ EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
new file mode 100644
index 0000000..83d74f9
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
@@ -0,0 +1,305 @@
+// Copyright 2015 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 "base/message_loop/message_loop.h"
+#include "build/build_config.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// The tests in this file are designed to test the interaction between a
+// Callback and its associated Binding. If a Callback is deleted before
+// being used we DCHECK fail--unless the associated Binding has already
+// been closed or deleted. This contract must be explained to the Mojo
+// application developer. For example it is the developer's responsibility to
+// ensure that the Binding is destroyed before an unused Callback is destroyed.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mojo {
+namespace test {
+namespace {
+
+// A Runnable object that saves the last value it sees via the
+// provided int32_t*. Used on the client side.
+class ValueSaver {
+ public:
+ explicit ValueSaver(int32_t* last_value_seen)
+ : last_value_seen_(last_value_seen) {}
+ void Run(int32_t x) const { *last_value_seen_ = x; }
+
+ private:
+ int32_t* const last_value_seen_;
+};
+
+// An implementation of sample::Provider used on the server side.
+// It only implements one of the methods: EchoInt().
+// All it does is save the values and Callbacks it sees.
+class InterfaceImpl : public sample::Provider {
+ public:
+ InterfaceImpl()
+ : last_server_value_seen_(0),
+ callback_saved_(new Callback<void(int32_t)>()) {}
+
+ ~InterfaceImpl() override {
+ if (callback_saved_) {
+ delete callback_saved_;
+ }
+ }
+
+ // Run's the callback previously saved from the last invocation
+ // of |EchoInt()|.
+ bool RunCallback() {
+ if (callback_saved_) {
+ callback_saved_->Run(last_server_value_seen_);
+ return true;
+ }
+ return false;
+ }
+
+ // Delete's the previously saved callback.
+ void DeleteCallback() {
+ delete callback_saved_;
+ callback_saved_ = nullptr;
+ }
+
+ // sample::Provider implementation
+
+ // Saves its two input values in member variables and does nothing else.
+ void EchoInt(int32_t x, const Callback<void(int32_t)>& callback) override {
+ last_server_value_seen_ = x;
+ *callback_saved_ = callback;
+ }
+
+ void EchoString(const String& a,
+ const Callback<void(String)>& callback) override {
+ MOJO_CHECK(false) << "Not implemented.";
+ }
+
+ void EchoStrings(const String& a,
+ const String& b,
+ const Callback<void(String, String)>& callback) override {
+ MOJO_CHECK(false) << "Not implemented.";
+ }
+
+ void EchoMessagePipeHandle(
+ ScopedMessagePipeHandle a,
+ const Callback<void(ScopedMessagePipeHandle)>& callback) override {
+ MOJO_CHECK(false) << "Not implemented.";
+ }
+
+ void EchoEnum(sample::Enum a,
+ const Callback<void(sample::Enum)>& callback) override {
+ MOJO_CHECK(false) << "Not implemented.";
+ }
+
+ void resetLastServerValueSeen() { last_server_value_seen_ = 0; }
+
+ int32_t last_server_value_seen() const { return last_server_value_seen_; }
+
+ private:
+ int32_t last_server_value_seen_;
+ Callback<void(int32_t)>* callback_saved_;
+};
+
+class BindingCallbackTest : public testing::Test {
+ public:
+ BindingCallbackTest() : loop_(common::MessagePumpMojo::Create()) {}
+ ~BindingCallbackTest() override {}
+
+ protected:
+ int32_t last_client_callback_value_seen_;
+ sample::ProviderPtr interface_ptr_;
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ private:
+ base::MessageLoop loop_;
+};
+
+// Tests that the InterfacePtr and the Binding can communicate with each
+// other normally.
+TEST_F(BindingCallbackTest, Basic) {
+ // Create the ServerImpl and the Binding.
+ InterfaceImpl server_impl;
+ Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now run the Callback.
+ server_impl.RunCallback();
+ PumpMessages();
+
+ // Check that the client has now seen the correct value.
+ EXPECT_EQ(7, last_client_callback_value_seen_);
+
+ // Initialize the test values again.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method again.
+ interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(13, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now run the Callback again.
+ server_impl.RunCallback();
+ PumpMessages();
+
+ // Check that the client has now seen the correct value again.
+ EXPECT_EQ(13, last_client_callback_value_seen_);
+}
+
+// Tests that running the Callback after the Binding has been deleted
+// results in a clean failure.
+TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) {
+ // Create the ServerImpl.
+ InterfaceImpl server_impl;
+ {
+ // Create the binding in an inner scope so it can be deleted first.
+ Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+ }
+ // The binding has now been destroyed and the pipe is closed.
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now try to run the Callback. This should do nothing since the pipe
+ // is closed.
+ EXPECT_TRUE(server_impl.RunCallback());
+ PumpMessages();
+
+ // Check that the client has still not seen the correct value.
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Attempt to invoke the method again and confirm that an error was
+ // encountered.
+ interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+ EXPECT_TRUE(interface_ptr_.encountered_error());
+}
+
+// Tests that deleting a Callback without running it after the corresponding
+// binding has already been deleted does not result in a crash.
+TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) {
+ // Create the ServerImpl.
+ InterfaceImpl server_impl;
+ {
+ // Create the binding in an inner scope so it can be deleted first.
+ Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+ }
+ // The binding has now been destroyed and the pipe is closed.
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Delete the callback without running it. This should not
+ // cause a problem because the insfrastructure can detect that the
+ // binding has already been destroyed and the pipe is closed.
+ server_impl.DeleteCallback();
+}
+
+// Tests that closing a Binding allows us to delete a callback
+// without running it without encountering a crash.
+TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) {
+ // Create the ServerImpl and the Binding.
+ InterfaceImpl server_impl;
+ Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+ // Now close the Binding.
+ binding.Close();
+
+ // Delete the callback without running it. This should not
+ // cause a crash because the insfrastructure can detect that the
+ // binding has already been closed.
+ server_impl.DeleteCallback();
+
+ // Check that the client has still not seen the correct value.
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+}
+
+// Tests that deleting a Callback without using it before the
+// Binding has been destroyed or closed results in a DCHECK.
+TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) {
+ // Create the ServerImpl and the Binding.
+ InterfaceImpl server_impl;
+ Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+
+ // Initialize the test values.
+ server_impl.resetLastServerValueSeen();
+ last_client_callback_value_seen_ = 0;
+
+ // Invoke the Echo method.
+ interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_));
+ PumpMessages();
+
+ // Check that server saw the correct value, but the client has not yet.
+ EXPECT_EQ(7, server_impl.last_server_value_seen());
+ EXPECT_EQ(0, last_client_callback_value_seen_);
+
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+ // Delete the callback without running it. This should cause a crash in debug
+ // builds due to a DCHECK.
+ std::string regex("Check failed: !callback_was_dropped.");
+#if defined(OS_WIN)
+ // TODO(msw): Fix MOJO_DCHECK logs and EXPECT_DEATH* on Win: crbug.com/535014
+ regex.clear();
+#endif // OS_WIN
+ EXPECT_DEATH_IF_SUPPORTED(server_impl.DeleteCallback(), regex.c_str());
+#endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/binding_unittest.cc b/mojo/public/cpp/bindings/tests/binding_unittest.cc
new file mode 100644
index 0000000..1462b33
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/binding_unittest.cc
@@ -0,0 +1,364 @@
+// Copyright 2015 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.
+
+// Note: This file tests both binding.h (mojo::Binding) and strong_binding.h
+// (mojo::StrongBinding).
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class BindingTestBase : public testing::Test {
+ public:
+ BindingTestBase() : loop_(common::MessagePumpMojo::Create()) {}
+ ~BindingTestBase() override {}
+
+ base::MessageLoop& loop() { return loop_; }
+
+ private:
+ base::MessageLoop loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(BindingTestBase);
+};
+
+class ServiceImpl : public sample::Service {
+ public:
+ explicit ServiceImpl(bool* was_deleted = nullptr)
+ : was_deleted_(was_deleted) {}
+ ~ServiceImpl() override {
+ if (was_deleted_)
+ *was_deleted_ = true;
+ }
+
+ private:
+ // sample::Service implementation
+ void Frobinate(sample::FooPtr foo,
+ BazOptions options,
+ sample::PortPtr port,
+ const FrobinateCallback& callback) override {
+ callback.Run(1);
+ }
+ void GetPort(InterfaceRequest<sample::Port> port) override {}
+
+ bool* const was_deleted_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
+};
+
+// BindingTest -----------------------------------------------------------------
+
+using BindingTest = BindingTestBase;
+
+TEST_F(BindingTest, Close) {
+ bool called = false;
+ sample::ServicePtr ptr;
+ auto request = GetProxy(&ptr);
+ ptr.set_connection_error_handler([&called]() { called = true; });
+ ServiceImpl impl;
+ Binding<sample::Service> binding(&impl, request.Pass());
+
+ binding.Close();
+ EXPECT_FALSE(called);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+}
+
+// Tests that destroying a mojo::Binding closes the bound message pipe handle.
+TEST_F(BindingTest, DestroyClosesMessagePipe) {
+ bool encountered_error = false;
+ ServiceImpl impl;
+ sample::ServicePtr ptr;
+ auto request = GetProxy(&ptr);
+ ptr.set_connection_error_handler(
+ [&encountered_error]() { encountered_error = true; });
+ bool called = false;
+ auto called_cb = [&called](int32_t result) { called = true; };
+ {
+ Binding<sample::Service> binding(&impl, request.Pass());
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ EXPECT_FALSE(encountered_error);
+ }
+ // Now that the Binding is out of scope we should detect an error on the other
+ // end of the pipe.
+ loop().RunUntilIdle();
+ EXPECT_TRUE(encountered_error);
+
+ // And calls should fail.
+ called = false;
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ loop().RunUntilIdle();
+ EXPECT_FALSE(called);
+}
+
+// Tests that the binding's connection error handler gets called when the other
+// end is closed.
+TEST_F(BindingTest, ConnectionError) {
+ bool called = false;
+ {
+ ServiceImpl impl;
+ sample::ServicePtr ptr;
+ Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+ binding.set_connection_error_handler([&called]() { called = true; });
+ ptr.reset();
+ EXPECT_FALSE(called);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ // We want to make sure that it isn't called again during destruction.
+ called = false;
+ }
+ EXPECT_FALSE(called);
+}
+
+// Tests that calling Close doesn't result in the connection error handler being
+// called.
+TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) {
+ ServiceImpl impl;
+ sample::ServicePtr ptr;
+ Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+ bool called = false;
+ binding.set_connection_error_handler([&called]() { called = true; });
+ binding.Close();
+ loop().RunUntilIdle();
+ EXPECT_FALSE(called);
+
+ // We can also close the other end, and the error handler still won't be
+ // called.
+ ptr.reset();
+ loop().RunUntilIdle();
+ EXPECT_FALSE(called);
+}
+
+class ServiceImplWithBinding : public ServiceImpl {
+ public:
+ ServiceImplWithBinding(bool* was_deleted,
+ InterfaceRequest<sample::Service> request)
+ : ServiceImpl(was_deleted), binding_(this, request.Pass()) {
+ binding_.set_connection_error_handler([this]() { delete this; });
+ }
+
+ private:
+ Binding<sample::Service> binding_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding);
+};
+
+// Tests that the binding may be deleted in the connection error handler.
+TEST_F(BindingTest, SelfDeleteOnConnectionError) {
+ bool was_deleted = false;
+ sample::ServicePtr ptr;
+ // This should delete itself on connection error.
+ new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr));
+ ptr.reset();
+ EXPECT_FALSE(was_deleted);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(was_deleted);
+}
+
+// Tests that explicitly calling Unbind followed by rebinding works.
+TEST_F(BindingTest, Unbind) {
+ ServiceImpl impl;
+ sample::ServicePtr ptr;
+ Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+
+ bool called = false;
+ auto called_cb = [&called](int32_t result) { called = true; };
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+
+ called = false;
+ auto request = binding.Unbind();
+ EXPECT_FALSE(binding.is_bound());
+ // All calls should fail when not bound...
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ loop().RunUntilIdle();
+ EXPECT_FALSE(called);
+
+ called = false;
+ binding.Bind(request.Pass());
+ EXPECT_TRUE(binding.is_bound());
+ // ...and should succeed again when the rebound.
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+}
+
+class IntegerAccessorImpl : public sample::IntegerAccessor {
+ public:
+ IntegerAccessorImpl() {}
+ ~IntegerAccessorImpl() override {}
+
+ private:
+ // sample::IntegerAccessor implementation.
+ void GetInteger(const GetIntegerCallback& callback) override {
+ callback.Run(1, sample::ENUM_VALUE);
+ }
+ void SetInteger(int64_t data, sample::Enum type) override {}
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl);
+};
+
+TEST_F(BindingTest, SetInterfacePtrVersion) {
+ IntegerAccessorImpl impl;
+ sample::IntegerAccessorPtr ptr;
+ Binding<sample::IntegerAccessor> binding(&impl, &ptr);
+ EXPECT_EQ(3u, ptr.version());
+}
+
+TEST_F(BindingTest, PauseResume) {
+ bool called = false;
+ auto called_cb = [&called](int32_t result) { called = true; };
+ sample::ServicePtr ptr;
+ auto request = GetProxy(&ptr);
+ ServiceImpl impl;
+ Binding<sample::Service> binding(&impl, request.Pass());
+ binding.PauseIncomingMethodCallProcessing();
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ EXPECT_FALSE(called);
+ loop().RunUntilIdle();
+ // Frobinate() should not be called as the binding is paused.
+ EXPECT_FALSE(called);
+
+ // Resume the binding, which should trigger processing.
+ binding.ResumeIncomingMethodCallProcessing();
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+}
+
+// Verifies the connection error handler is not run while a binding is paused.
+TEST_F(BindingTest, ErrorHandleNotRunWhilePaused) {
+ bool called = false;
+ sample::ServicePtr ptr;
+ auto request = GetProxy(&ptr);
+ ServiceImpl impl;
+ Binding<sample::Service> binding(&impl, request.Pass());
+ binding.set_connection_error_handler([&called]() { called = true; });
+ binding.PauseIncomingMethodCallProcessing();
+
+ ptr.reset();
+ loop().RunUntilIdle();
+ // The connection error handle should not be called as the binding is paused.
+ EXPECT_FALSE(called);
+
+ // Resume the binding, which should trigger the error handler.
+ binding.ResumeIncomingMethodCallProcessing();
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+}
+
+// StrongBindingTest -----------------------------------------------------------
+
+using StrongBindingTest = BindingTestBase;
+
+// Tests that destroying a mojo::StrongBinding closes the bound message pipe
+// handle but does *not* destroy the implementation object.
+TEST_F(StrongBindingTest, DestroyClosesMessagePipe) {
+ bool encountered_error = false;
+ bool was_deleted = false;
+ ServiceImpl impl(&was_deleted);
+ sample::ServicePtr ptr;
+ auto request = GetProxy(&ptr);
+ ptr.set_connection_error_handler(
+ [&encountered_error]() { encountered_error = true; });
+ bool called = false;
+ auto called_cb = [&called](int32_t result) { called = true; };
+ {
+ StrongBinding<sample::Service> binding(&impl, request.Pass());
+ ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ called_cb);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ EXPECT_FALSE(encountered_error);
+ }
+ // Now that the StrongBinding is out of scope we should detect an error on the
+ // other end of the pipe.
+ loop().RunUntilIdle();
+ EXPECT_TRUE(encountered_error);
+ // But destroying the StrongBinding doesn't destroy the object.
+ ASSERT_FALSE(was_deleted);
+}
+
+class ServiceImplWithStrongBinding : public ServiceImpl {
+ public:
+ ServiceImplWithStrongBinding(bool* was_deleted,
+ InterfaceRequest<sample::Service> request)
+ : ServiceImpl(was_deleted), binding_(this, request.Pass()) {}
+
+ StrongBinding<sample::Service>& binding() { return binding_; }
+
+ private:
+ StrongBinding<sample::Service> binding_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding);
+};
+
+// Tests the typical case, where the implementation object owns the
+// StrongBinding (and should be destroyed on connection error).
+TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) {
+ sample::ServicePtr ptr;
+ bool was_deleted = false;
+ // Will delete itself.
+ new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr));
+
+ loop().RunUntilIdle();
+ EXPECT_FALSE(was_deleted);
+
+ ptr.reset();
+ EXPECT_FALSE(was_deleted);
+ loop().RunUntilIdle();
+ EXPECT_TRUE(was_deleted);
+}
+
+// Tests that even when the implementation object owns the StrongBinding, that
+// the implementation can still be deleted (which should result in the message
+// pipe being closed). Also checks that the connection error handler doesn't get
+// called.
+TEST_F(StrongBindingTest, ExplicitDeleteImpl) {
+ bool ptr_error_handler_called = false;
+ sample::ServicePtr ptr;
+ auto request = GetProxy(&ptr);
+ ptr.set_connection_error_handler(
+ [&ptr_error_handler_called]() { ptr_error_handler_called = true; });
+ bool was_deleted = false;
+ ServiceImplWithStrongBinding* impl =
+ new ServiceImplWithStrongBinding(&was_deleted, request.Pass());
+ bool binding_error_handler_called = false;
+ impl->binding().set_connection_error_handler(
+ [&binding_error_handler_called]() {
+ binding_error_handler_called = true;
+ });
+
+ loop().RunUntilIdle();
+ EXPECT_FALSE(ptr_error_handler_called);
+ EXPECT_FALSE(was_deleted);
+
+ delete impl;
+ EXPECT_FALSE(ptr_error_handler_called);
+ EXPECT_TRUE(was_deleted);
+ was_deleted = false; // It shouldn't be double-deleted!
+ loop().RunUntilIdle();
+ EXPECT_TRUE(ptr_error_handler_called);
+ EXPECT_FALSE(was_deleted);
+
+ EXPECT_FALSE(binding_error_handler_called);
+}
+
+} // namespace
+} // mojo
diff --git a/mojo/public/cpp/bindings/tests/bindings_perftest.cc b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
new file mode 100644
index 0000000..7440acc
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
@@ -0,0 +1,127 @@
+// Copyright 2015 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/binding.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const double kMojoTicksPerSecond = 1000000.0;
+
+double MojoTicksToSeconds(MojoTimeTicks ticks) {
+ return ticks / kMojoTicksPerSecond;
+}
+
+class PingServiceImpl : public test::PingService {
+ public:
+ explicit PingServiceImpl() {}
+ ~PingServiceImpl() override {}
+
+ // |PingService| methods:
+ void Ping(const Callback<void()>& callback) override;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
+};
+
+void PingServiceImpl::Ping(const Callback<void()>& callback) {
+ callback.Run();
+}
+
+class PingPongTest {
+ public:
+ explicit PingPongTest(test::PingServicePtr service);
+
+ void Run(unsigned int iterations);
+
+ private:
+ void OnPingDone();
+
+ test::PingServicePtr service_;
+ unsigned int iterations_to_run_;
+ unsigned int current_iterations_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(PingPongTest);
+};
+
+PingPongTest::PingPongTest(test::PingServicePtr service)
+ : service_(service.Pass()) {
+}
+
+void PingPongTest::Run(unsigned int iterations) {
+ iterations_to_run_ = iterations;
+ current_iterations_ = 0;
+
+ service_->Ping([this]() { OnPingDone(); });
+ RunLoop::current()->Run();
+}
+
+void PingPongTest::OnPingDone() {
+ current_iterations_++;
+ if (current_iterations_ >= iterations_to_run_) {
+ RunLoop::current()->Quit();
+ return;
+ }
+
+ service_->Ping([this]() { OnPingDone(); });
+}
+
+struct BoundPingService {
+ BoundPingService() : binding(&impl) {
+ binding.Bind(GetProxy(&service));
+ }
+
+ PingServiceImpl impl;
+ test::PingServicePtr service;
+ Binding<test::PingService> binding;
+};
+
+class MojoBindingsPerftest : public testing::Test {
+ protected:
+ Environment env_;
+ RunLoop run_loop_;
+};
+
+TEST_F(MojoBindingsPerftest, InProcessPingPong) {
+ test::PingServicePtr service;
+ PingServiceImpl impl;
+ Binding<test::PingService> binding(&impl, GetProxy(&service));
+ PingPongTest test(service.Pass());
+
+ {
+ const unsigned int kIterations = 100000;
+ const MojoTimeTicks start_time = MojoGetTimeTicksNow();
+ test.Run(kIterations);
+ const MojoTimeTicks end_time = MojoGetTimeTicksNow();
+ test::LogPerfResult(
+ "InProcessPingPong", "0_Inactive",
+ kIterations / MojoTicksToSeconds(end_time - start_time),
+ "pings/second");
+ }
+
+ {
+ const size_t kNumInactiveServices = 1000;
+ BoundPingService* inactive_services =
+ new BoundPingService[kNumInactiveServices];
+
+ const unsigned int kIterations = 10000;
+ const MojoTimeTicks start_time = MojoGetTimeTicksNow();
+ test.Run(kIterations);
+ const MojoTimeTicks end_time = MojoGetTimeTicksNow();
+ test::LogPerfResult(
+ "InProcessPingPong", "1000_Inactive",
+ kIterations / MojoTicksToSeconds(end_time - start_time),
+ "pings/second");
+
+ delete[] inactive_services;
+ }
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc
new file mode 100644
index 0000000..c1ec7b3
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2014 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 <limits>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+const void* ToPtr(uintptr_t ptr) {
+ return reinterpret_cast<const void*>(ptr);
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+TEST(BoundsCheckerTest, ConstructorRangeOverflow) {
+ {
+ // Test memory range overflow.
+ internal::BoundsChecker checker(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0);
+
+ EXPECT_FALSE(checker.IsValidRange(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
+ EXPECT_FALSE(checker.ClaimMemory(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
+ }
+
+ if (sizeof(size_t) > sizeof(uint32_t)) {
+ // Test handle index range overflow.
+ size_t num_handles =
+ static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5;
+ internal::BoundsChecker checker(ToPtr(0), 0, num_handles);
+
+ EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+ EXPECT_FALSE(
+ checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1)));
+
+ EXPECT_TRUE(
+ checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+ }
+}
+#endif
+
+TEST(BoundsCheckerTest, IsValidRange) {
+ {
+ internal::BoundsChecker checker(ToPtr(1234), 100, 0);
+
+ // Basics.
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(100), 5));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1230), 50));
+ EXPECT_TRUE(checker.IsValidRange(ToPtr(1234), 5));
+ EXPECT_TRUE(checker.IsValidRange(ToPtr(1240), 50));
+ EXPECT_TRUE(checker.IsValidRange(ToPtr(1234), 100));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 101));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1240), 100));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1333), 5));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(2234), 5));
+
+ // ClaimMemory() updates the valid range.
+ EXPECT_TRUE(checker.ClaimMemory(ToPtr(1254), 10));
+
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 1));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1254), 10));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1263), 1));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1263), 10));
+ EXPECT_TRUE(checker.IsValidRange(ToPtr(1264), 10));
+ EXPECT_TRUE(checker.IsValidRange(ToPtr(1264), 70));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1264), 71));
+ }
+
+ {
+ internal::BoundsChecker checker(ToPtr(1234), 100, 0);
+ // Should return false for empty ranges.
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(0), 0));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1200), 0));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 0));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1240), 0));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(2234), 0));
+ }
+
+ {
+ // The valid memory range is empty.
+ internal::BoundsChecker checker(ToPtr(1234), 0, 0);
+
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 1));
+ EXPECT_FALSE(checker.IsValidRange(ToPtr(1234), 0));
+ }
+
+ {
+ internal::BoundsChecker checker(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0);
+
+ // Test overflow.
+ EXPECT_FALSE(checker.IsValidRange(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 4000));
+ EXPECT_FALSE(checker.IsValidRange(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 1500),
+ std::numeric_limits<uint32_t>::max()));
+
+ // This should be fine.
+ EXPECT_TRUE(checker.IsValidRange(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 1500), 200));
+ }
+}
+
+TEST(BoundsCheckerTest, ClaimHandle) {
+ {
+ internal::BoundsChecker checker(ToPtr(0), 0, 10);
+
+ // Basics.
+ EXPECT_TRUE(checker.ClaimHandle(Handle(0)));
+ EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+
+ EXPECT_TRUE(checker.ClaimHandle(Handle(9)));
+ EXPECT_FALSE(checker.ClaimHandle(Handle(10)));
+
+ // Should fail because it is smaller than the max index that has been
+ // claimed.
+ EXPECT_FALSE(checker.ClaimHandle(Handle(8)));
+
+ // Should return true for invalid handle.
+ EXPECT_TRUE(
+ checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+ EXPECT_TRUE(
+ checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+ }
+
+ {
+ // No handle to claim.
+ internal::BoundsChecker checker(ToPtr(0), 0, 0);
+
+ EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+
+ // Should still return true for invalid handle.
+ EXPECT_TRUE(
+ checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+ }
+
+ {
+ // Test the case that |num_handles| is the same value as
+ // |internal::kEncodedInvalidHandleValue|.
+ EXPECT_EQ(internal::kEncodedInvalidHandleValue,
+ std::numeric_limits<uint32_t>::max());
+ internal::BoundsChecker checker(
+ ToPtr(0), 0, std::numeric_limits<uint32_t>::max());
+
+ EXPECT_TRUE(
+ checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1)));
+ EXPECT_FALSE(
+ checker.ClaimHandle(Handle(std::numeric_limits<uint32_t>::max() - 1)));
+ EXPECT_FALSE(checker.ClaimHandle(Handle(0)));
+
+ // Should still return true for invalid handle.
+ EXPECT_TRUE(
+ checker.ClaimHandle(Handle(internal::kEncodedInvalidHandleValue)));
+ }
+}
+
+TEST(BoundsCheckerTest, ClaimMemory) {
+ {
+ internal::BoundsChecker checker(ToPtr(1000), 2000, 0);
+
+ // Basics.
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(500), 100));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(800), 300));
+ EXPECT_TRUE(checker.ClaimMemory(ToPtr(1000), 100));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(1099), 100));
+ EXPECT_TRUE(checker.ClaimMemory(ToPtr(1100), 200));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(2000), 1001));
+ EXPECT_TRUE(checker.ClaimMemory(ToPtr(2000), 500));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(2000), 500));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(1400), 100));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(3000), 1));
+ EXPECT_TRUE(checker.ClaimMemory(ToPtr(2500), 500));
+ }
+
+ {
+ // No memory to claim.
+ internal::BoundsChecker checker(ToPtr(10000), 0, 0);
+
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(10000), 1));
+ EXPECT_FALSE(checker.ClaimMemory(ToPtr(10000), 0));
+ }
+
+ {
+ internal::BoundsChecker checker(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0);
+
+ // Test overflow.
+ EXPECT_FALSE(checker.ClaimMemory(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 4000));
+ EXPECT_FALSE(
+ checker.ClaimMemory(ToPtr(std::numeric_limits<uintptr_t>::max() - 750),
+ std::numeric_limits<uint32_t>::max()));
+
+ // This should be fine.
+ EXPECT_TRUE(checker.ClaimMemory(
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 750), 200));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/buffer_unittest.cc b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
new file mode 100644
index 0000000..317a2a5
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2014 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 <limits>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+bool IsZero(void* p_buf, size_t size) {
+ char* buf = reinterpret_cast<char*>(p_buf);
+ for (size_t i = 0; i < size; ++i) {
+ if (buf[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+// Tests that FixedBuffer allocates memory aligned to 8 byte boundaries.
+TEST(FixedBufferTest, Alignment) {
+ internal::FixedBufferForTesting buf(internal::Align(10) * 2);
+ ASSERT_EQ(buf.size(), 16u * 2);
+
+ void* a = buf.Allocate(10);
+ ASSERT_TRUE(a);
+ EXPECT_TRUE(IsZero(a, 10));
+ EXPECT_EQ(0, reinterpret_cast<ptrdiff_t>(a) % 8);
+
+ void* b = buf.Allocate(10);
+ ASSERT_TRUE(b);
+ EXPECT_TRUE(IsZero(b, 10));
+ EXPECT_EQ(0, reinterpret_cast<ptrdiff_t>(b) % 8);
+
+ // Any more allocations would result in an assert, but we can't test that.
+}
+
+// Tests that FixedBufferForTesting::Leak passes ownership to the caller.
+TEST(FixedBufferTest, Leak) {
+ void* ptr = nullptr;
+ void* buf_ptr = nullptr;
+ {
+ internal::FixedBufferForTesting buf(8);
+ ASSERT_EQ(8u, buf.size());
+
+ ptr = buf.Allocate(8);
+ ASSERT_TRUE(ptr);
+ buf_ptr = buf.Leak();
+
+ // The buffer should point to the first element allocated.
+ // TODO(mpcomplete): Is this a reasonable expectation?
+ EXPECT_EQ(ptr, buf_ptr);
+
+ // The FixedBufferForTesting should be empty now.
+ EXPECT_EQ(0u, buf.size());
+ EXPECT_FALSE(buf.Leak());
+ }
+
+ // Since we called Leak, ptr is still writable after FixedBufferForTesting
+ // went out of scope.
+ memset(ptr, 1, 8);
+ free(buf_ptr);
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+TEST(FixedBufferTest, TooBig) {
+ internal::FixedBufferForTesting buf(24);
+
+ // A little bit too large.
+ EXPECT_EQ(reinterpret_cast<void*>(0), buf.Allocate(32));
+
+ // Move the cursor forward.
+ EXPECT_NE(reinterpret_cast<void*>(0), buf.Allocate(16));
+
+ // A lot too large.
+ EXPECT_EQ(reinterpret_cast<void*>(0),
+ buf.Allocate(std::numeric_limits<size_t>::max() - 1024u));
+
+ // A lot too large, leading to possible integer overflow.
+ EXPECT_EQ(reinterpret_cast<void*>(0),
+ buf.Allocate(std::numeric_limits<size_t>::max() - 8u));
+}
+#endif
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/callback_unittest.cc b/mojo/public/cpp/bindings/tests/callback_unittest.cc
new file mode 100644
index 0000000..158b21e
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/callback_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2015 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/callback.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+struct RunnableNoArgs {
+ explicit RunnableNoArgs(int* calls) : calls(calls) {}
+
+ void Run() const { (*calls)++; }
+ int* calls;
+};
+
+struct RunnableOneArg {
+ explicit RunnableOneArg(int* calls) : calls(calls) {}
+
+ void Run(int increment) const { (*calls) += increment; }
+
+ int* calls;
+};
+
+struct RunnableStringArgByConstRef {
+ explicit RunnableStringArgByConstRef(int* calls) : calls(calls) {}
+
+ void Run(const String& s) const { (*calls)++; }
+
+ int* calls;
+};
+
+using ExampleMoveOnlyType = Map<int, int>;
+
+struct RunnableMoveOnlyParam {
+ explicit RunnableMoveOnlyParam(int* calls) : calls(calls) {}
+
+ void Run(ExampleMoveOnlyType m) const { (*calls)++; }
+
+ int* calls;
+};
+
+int* g_calls = nullptr;
+
+void FunctionNoArgs() {
+ (*g_calls)++;
+}
+
+void FunctionOneArg(int increment) {
+ (*g_calls) += increment;
+}
+
+void FunctionStringArgByConstRef(const String& s) {
+ (*g_calls)++;
+}
+
+void FunctionMoveOnlyType(ExampleMoveOnlyType m) {
+ (*g_calls)++;
+}
+
+static_assert(!internal::HasCompatibleCallOperator<RunnableNoArgs>::value,
+ "HasCompatibleCallOperator<Runnable>");
+static_assert(!internal::HasCompatibleCallOperator<RunnableOneArg, int>::value,
+ "HasCompatibleCallOperator<RunnableOneArg, int>");
+static_assert(!internal::HasCompatibleCallOperator<RunnableStringArgByConstRef,
+ String>::value,
+ "HasCompatibleCallOperator<RunnableStringArgByConstRef, String>");
+static_assert(!internal::HasCompatibleCallOperator<RunnableMoveOnlyParam,
+ ExampleMoveOnlyType>::value,
+ "HasCompatibleCallOperator<RunnableMoveOnlyParam, String>");
+
+auto lambda_one = []() {};
+static_assert(internal::HasCompatibleCallOperator<decltype(lambda_one)>::value,
+ "HasCompatibleCallOperator<lambda []() {}>");
+
+auto lambda_two = [](int x) {};
+static_assert(
+ internal::HasCompatibleCallOperator<decltype(lambda_two), int>::value,
+ "HasCompatibleCallOperator<lambda [](int x) {}, int>");
+
+auto lambda_three = [](const String& s) {};
+static_assert(
+ internal::HasCompatibleCallOperator<decltype(lambda_three), String>::value,
+ "HasCompatibleCallOperator<lambda [](const String& s) {}, String>");
+
+auto lambda_four = [](ExampleMoveOnlyType m) {};
+static_assert(internal::HasCompatibleCallOperator<decltype(lambda_four),
+ ExampleMoveOnlyType>::value,
+ "HasCompatibleCallOperator<lambda [](ExampleMoveOnlyType) {}, "
+ "ExampleMoveOnlyType>");
+
+// Tests constructing and invoking a mojo::Callback from objects with a
+// compatible Run() method (called 'runnables'), from lambdas, and from function
+// pointers.
+TEST(Callback, Create) {
+ int calls = 0;
+
+ RunnableNoArgs f(&calls);
+ // Construct from a runnable object.
+ mojo::Callback<void()> cb = f;
+ cb.Run();
+ EXPECT_EQ(1, calls);
+
+ // Construct from a parameterless lambda that captures one variable.
+ cb = [&calls]() { calls++; };
+ cb.Run();
+ EXPECT_EQ(2, calls);
+
+ // Construct from a runnable object with one primitive parameter.
+ mojo::Callback<void(int)> cb_with_param = RunnableOneArg(&calls);
+ cb_with_param.Run(1);
+ EXPECT_EQ(3, calls);
+
+ // Construct from a lambda that takes one parameter and captures one variable.
+ cb_with_param = [&calls](int increment) { calls += increment; };
+ cb_with_param.Run(1);
+ EXPECT_EQ(4, calls);
+
+ // Construct from a runnable object with one string parameter.
+ mojo::Callback<void(String)> cb_with_string_param =
+ RunnableStringArgByConstRef(&calls);
+ cb_with_string_param.Run(String("hello world"));
+ EXPECT_EQ(5, calls);
+
+ // Construct from a lambda that takes one string parameter.
+ cb_with_string_param = [&calls](const String& s) { calls++; };
+ cb_with_string_param.Run(String("world"));
+ EXPECT_EQ(6, calls);
+
+ ExampleMoveOnlyType m;
+ mojo::Callback<void(ExampleMoveOnlyType)> cb_with_move_only_param =
+ RunnableMoveOnlyParam(&calls);
+ cb_with_move_only_param.Run(m.Clone());
+ EXPECT_EQ(7, calls);
+
+ cb_with_move_only_param = [&calls](ExampleMoveOnlyType m) { calls++; };
+ cb_with_move_only_param.Run(m.Clone());
+ EXPECT_EQ(8, calls);
+
+ // Construct from a function pointer.
+ g_calls = &calls;
+
+ cb = &FunctionNoArgs;
+ cb.Run();
+ EXPECT_EQ(9, calls);
+
+ cb_with_param = &FunctionOneArg;
+ cb_with_param.Run(1);
+ EXPECT_EQ(10, calls);
+
+ cb_with_string_param = &FunctionStringArgByConstRef;
+ cb_with_string_param.Run(String("hello"));
+ EXPECT_EQ(11, calls);
+
+ cb_with_move_only_param = &FunctionMoveOnlyType;
+ cb_with_move_only_param.Run(m.Clone());
+ EXPECT_EQ(12, calls);
+
+ g_calls = nullptr;
+}
+
+bool g_overloaded_function_with_int_param_called = false;
+
+void OverloadedFunction(int param) {
+ g_overloaded_function_with_int_param_called = true;
+}
+
+bool g_overloaded_function_with_double_param_called = false;
+
+void OverloadedFunction(double param) {
+ g_overloaded_function_with_double_param_called = true;
+}
+
+// Tests constructing and invoking a mojo::Callback from pointers to overloaded
+// functions.
+TEST(Callback, CreateFromOverloadedFunctionPtr) {
+ g_overloaded_function_with_int_param_called = false;
+ mojo::Callback<void(int)> cb_with_int_param = &OverloadedFunction;
+ cb_with_int_param.Run(123);
+ EXPECT_TRUE(g_overloaded_function_with_int_param_called);
+ g_overloaded_function_with_int_param_called = false;
+
+ g_overloaded_function_with_double_param_called = false;
+ mojo::Callback<void(double)> cb_with_double_param = &OverloadedFunction;
+ cb_with_double_param.Run(123);
+ EXPECT_TRUE(g_overloaded_function_with_double_param_called);
+ g_overloaded_function_with_double_param_called = false;
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
new file mode 100644
index 0000000..febc72e
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -0,0 +1,443 @@
+// Copyright 2013 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 <stdlib.h>
+#include <string.h>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/tests/message_queue.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+ MessageAccumulator() {}
+
+ bool Accept(Message* message) override {
+ queue_.Push(message);
+ return true;
+ }
+
+ bool IsEmpty() const { return queue_.IsEmpty(); }
+
+ void Pop(Message* message) { queue_.Pop(message); }
+
+ private:
+ MessageQueue queue_;
+};
+
+class ConnectorDeletingMessageAccumulator : public MessageAccumulator {
+ public:
+ ConnectorDeletingMessageAccumulator(internal::Connector** connector)
+ : connector_(connector) {}
+
+ bool Accept(Message* message) override {
+ delete *connector_;
+ *connector_ = nullptr;
+ return MessageAccumulator::Accept(message);
+ }
+
+ private:
+ internal::Connector** connector_;
+};
+
+class ReentrantMessageAccumulator : public MessageAccumulator {
+ public:
+ ReentrantMessageAccumulator(internal::Connector* connector)
+ : connector_(connector), number_of_calls_(0) {}
+
+ bool Accept(Message* message) override {
+ if (!MessageAccumulator::Accept(message))
+ return false;
+ number_of_calls_++;
+ if (number_of_calls_ == 1) {
+ return connector_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+ }
+ return true;
+ }
+
+ int number_of_calls() { return number_of_calls_; }
+
+ private:
+ internal::Connector* connector_;
+ int number_of_calls_;
+};
+
+class ConnectorTest : public testing::Test {
+ public:
+ ConnectorTest() : loop_(common::MessagePumpMojo::Create()) {}
+
+ void SetUp() override {
+ CreateMessagePipe(nullptr, &handle0_, &handle1_);
+ }
+
+ void TearDown() override {}
+
+ void AllocMessage(const char* text, Message* message) {
+ size_t payload_size = strlen(text) + 1; // Plus null terminator.
+ internal::MessageBuilder builder(1, payload_size);
+ memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
+
+ builder.message()->MoveTo(message);
+ }
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ protected:
+ ScopedMessagePipeHandle handle0_;
+ ScopedMessagePipeHandle handle1_;
+
+ private:
+ base::MessageLoop loop_;
+};
+
+TEST_F(ConnectorTest, Basic) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ const char kText[] = "hello world";
+
+ Message message;
+ AllocMessage(kText, &message);
+
+ connector0.Accept(&message);
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ PumpMessages();
+
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, Basic_Synchronous) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ const char kText[] = "hello world";
+
+ Message message;
+ AllocMessage(kText, &message);
+
+ connector0.Accept(&message);
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ connector1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, Basic_EarlyIncomingReceiver) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ const char kText[] = "hello world";
+
+ Message message;
+ AllocMessage(kText, &message);
+
+ connector0.Accept(&message);
+
+ PumpMessages();
+
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, Basic_TwoMessages) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ const char* kText[] = {"hello", "world"};
+
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+ Message message;
+ AllocMessage(kText[i], &message);
+
+ connector0.Accept(&message);
+ }
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ PumpMessages();
+
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText[i]),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+ }
+}
+
+TEST_F(ConnectorTest, Basic_TwoMessages_Synchronous) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ const char* kText[] = {"hello", "world"};
+
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+ Message message;
+ AllocMessage(kText[i], &message);
+
+ connector0.Accept(&message);
+ }
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ connector1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText[0]),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+
+ ASSERT_TRUE(accumulator.IsEmpty());
+}
+
+TEST_F(ConnectorTest, WriteToClosedPipe) {
+ internal::Connector connector0(handle0_.Pass());
+
+ const char kText[] = "hello world";
+
+ Message message;
+ AllocMessage(kText, &message);
+
+ // Close the other end of the pipe.
+ handle1_.reset();
+
+ // Not observed yet because we haven't spun the message loop yet.
+ EXPECT_FALSE(connector0.encountered_error());
+
+ // Write failures are not reported.
+ bool ok = connector0.Accept(&message);
+ EXPECT_TRUE(ok);
+
+ // Still not observed.
+ EXPECT_FALSE(connector0.encountered_error());
+
+ // Spin the message loop, and then we should start observing the closed pipe.
+ PumpMessages();
+
+ EXPECT_TRUE(connector0.encountered_error());
+}
+
+TEST_F(ConnectorTest, MessageWithHandles) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ const char kText[] = "hello world";
+
+ Message message1;
+ AllocMessage(kText, &message1);
+
+ MessagePipe pipe;
+ message1.mutable_handles()->push_back(pipe.handle0.release());
+
+ connector0.Accept(&message1);
+
+ // The message should have been transferred, releasing the handles.
+ EXPECT_TRUE(message1.handles()->empty());
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ PumpMessages();
+
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+ ASSERT_EQ(1U, message_received.handles()->size());
+
+ // Now send a message to the transferred handle and confirm it's sent through
+ // to the orginal pipe.
+ // TODO(vtl): Do we need a better way of "downcasting" the handle types?
+ ScopedMessagePipeHandle smph;
+ smph.reset(MessagePipeHandle(message_received.handles()->front().value()));
+ message_received.mutable_handles()->front() = Handle();
+ // |smph| now owns this handle.
+
+ internal::Connector connector_received(smph.Pass());
+ internal::Connector connector_original(pipe.handle1.Pass());
+
+ Message message2;
+ AllocMessage(kText, &message2);
+
+ connector_received.Accept(&message2);
+ connector_original.set_incoming_receiver(&accumulator);
+ PumpMessages();
+
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithError) {
+ internal::Connector connector0(handle0_.Pass());
+ // Close the other end of the pipe.
+ handle1_.reset();
+ ASSERT_FALSE(connector0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithDeletion) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector* connector1 = new internal::Connector(handle1_.Pass());
+
+ const char kText[] = "hello world";
+
+ Message message;
+ AllocMessage(kText, &message);
+
+ connector0.Accept(&message);
+
+ ConnectorDeletingMessageAccumulator accumulator(&connector1);
+ connector1->set_incoming_receiver(&accumulator);
+
+ connector1->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+ ASSERT_FALSE(connector1);
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+}
+
+TEST_F(ConnectorTest, WaitForIncomingMessageWithReentrancy) {
+ internal::Connector connector0(handle0_.Pass());
+ internal::Connector connector1(handle1_.Pass());
+
+ const char* kText[] = {"hello", "world"};
+
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+ Message message;
+ AllocMessage(kText[i], &message);
+
+ connector0.Accept(&message);
+ }
+
+ ReentrantMessageAccumulator accumulator(&connector1);
+ connector1.set_incoming_receiver(&accumulator);
+
+ PumpMessages();
+
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText[i]),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+ }
+
+ ASSERT_EQ(2, accumulator.number_of_calls());
+}
+
+TEST_F(ConnectorTest, RaiseError) {
+ internal::Connector connector0(handle0_.Pass());
+ bool error_handler_called0 = false;
+ connector0.set_connection_error_handler(
+ [&error_handler_called0]() { error_handler_called0 = true; });
+
+ internal::Connector connector1(handle1_.Pass());
+ bool error_handler_called1 = false;
+ connector1.set_connection_error_handler(
+ [&error_handler_called1]() { error_handler_called1 = true; });
+
+ const char kText[] = "hello world";
+
+ Message message;
+ AllocMessage(kText, &message);
+
+ connector0.Accept(&message);
+ connector0.RaiseError();
+
+ MessageAccumulator accumulator;
+ connector1.set_incoming_receiver(&accumulator);
+
+ PumpMessages();
+
+ // Messages sent prior to RaiseError() still arrive at the other end.
+ ASSERT_FALSE(accumulator.IsEmpty());
+
+ Message message_received;
+ accumulator.Pop(&message_received);
+
+ EXPECT_EQ(
+ std::string(kText),
+ std::string(reinterpret_cast<const char*>(message_received.payload())));
+
+ PumpMessages();
+
+ // Connection error handler is called at both sides.
+ EXPECT_TRUE(error_handler_called0);
+ EXPECT_TRUE(error_handler_called1);
+
+ // The error flag is set at both sides.
+ EXPECT_TRUE(connector0.encountered_error());
+ EXPECT_TRUE(connector1.encountered_error());
+
+ // The message pipe handle is valid at both sides.
+ EXPECT_TRUE(connector0.is_valid());
+ EXPECT_TRUE(connector1.is_valid());
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/constant_unittest.cc b/mojo/public/cpp/bindings/tests/constant_unittest.cc
new file mode 100644
index 0000000..f6394f3
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/constant_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 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/interfaces/bindings/tests/test_constants.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+TEST(ConstantTest, GlobalConstants) {
+ // Compile-time constants.
+ static_assert(kBoolValue == true, "");
+ static_assert(kInt8Value == -2, "");
+ static_assert(kUint8Value == 128U, "");
+ static_assert(kInt16Value == -233, "");
+ static_assert(kUint16Value == 44204U, "");
+ static_assert(kInt32Value == -44204, "");
+ static_assert(kUint32Value == 4294967295U, "");
+ static_assert(kInt64Value == -9223372036854775807, "");
+ static_assert(kUint64Value == 9999999999999999999ULL, "");
+
+ EXPECT_DOUBLE_EQ(kDoubleValue, 3.14159);
+ EXPECT_FLOAT_EQ(kFloatValue, 2.71828f);
+}
+
+TEST(ConstantTest, StructConstants) {
+ // Compile-time constants.
+ static_assert(StructWithConstants::kInt8Value == 5U, "");
+
+ EXPECT_FLOAT_EQ(StructWithConstants::kFloatValue, 765.432f);
+}
+
+TEST(ConstantTest, InterfaceConstants) {
+ // Compile-time constants.
+ static_assert(InterfaceWithConstants::kUint32Value == 20100722, "");
+
+ EXPECT_DOUBLE_EQ(InterfaceWithConstants::kDoubleValue, 12.34567);
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/container_test_util.cc b/mojo/public/cpp/bindings/tests/container_test_util.cc
new file mode 100644
index 0000000..e8377c4
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/container_test_util.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 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/tests/container_test_util.h"
+
+namespace mojo {
+
+size_t CopyableType::num_instances_ = 0;
+size_t MoveOnlyType::num_instances_ = 0;
+
+CopyableType::CopyableType() : copied_(false), ptr_(this) {
+ num_instances_++;
+}
+
+CopyableType::CopyableType(const CopyableType& other)
+ : copied_(true), ptr_(other.ptr()) {
+ num_instances_++;
+}
+
+CopyableType& CopyableType::operator=(const CopyableType& other) {
+ copied_ = true;
+ ptr_ = other.ptr();
+ return *this;
+}
+
+CopyableType::~CopyableType() {
+ num_instances_--;
+}
+
+MoveOnlyType::MoveOnlyType() : moved_(false), ptr_(this) {
+ num_instances_++;
+}
+
+MoveOnlyType::MoveOnlyType(MoveOnlyType&& other)
+ : moved_(true), ptr_(other.ptr()) {
+ num_instances_++;
+}
+
+MoveOnlyType& MoveOnlyType::operator=(MoveOnlyType&& other) {
+ moved_ = true;
+ ptr_ = other.ptr();
+ return *this;
+}
+
+MoveOnlyType::~MoveOnlyType() {
+ num_instances_--;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/container_test_util.h b/mojo/public/cpp/bindings/tests/container_test_util.h
new file mode 100644
index 0000000..1e29d22
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/container_test_util.h
@@ -0,0 +1,52 @@
+// Copyright 2014 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_TESTS_CONTAINER_TEST_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class CopyableType {
+ public:
+ CopyableType();
+ CopyableType(const CopyableType& other);
+ CopyableType& operator=(const CopyableType& other);
+ ~CopyableType();
+
+ bool copied() const { return copied_; }
+ static size_t num_instances() { return num_instances_; }
+ CopyableType* ptr() const { return ptr_; }
+ void ResetCopied() { copied_ = false; }
+
+ private:
+ bool copied_;
+ static size_t num_instances_;
+ CopyableType* ptr_;
+};
+
+class MoveOnlyType {
+ MOJO_MOVE_ONLY_TYPE(MoveOnlyType)
+ public:
+ typedef MoveOnlyType Data_;
+ MoveOnlyType();
+ MoveOnlyType(MoveOnlyType&& other);
+ MoveOnlyType& operator=(MoveOnlyType&& other);
+ ~MoveOnlyType();
+
+ bool moved() const { return moved_; }
+ static size_t num_instances() { return num_instances_; }
+ MoveOnlyType* ptr() const { return ptr_; }
+ void ResetMoved() { moved_ = false; }
+
+ private:
+ bool moved_;
+ static size_t num_instances_;
+ MoveOnlyType* ptr_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_CONTAINER_TEST_UTIL_H_
diff --git a/mojo/public/cpp/bindings/tests/equals_unittest.cc b/mojo/public/cpp/bindings/tests/equals_unittest.cc
new file mode 100644
index 0000000..5306f1f
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/equals_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 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/lib/value_traits.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+namespace {
+
+RectPtr CreateRect() {
+ RectPtr r = Rect::New();
+ r->x = 1;
+ r->y = 2;
+ r->width = 3;
+ r->height = 4;
+ return r.Pass();
+}
+
+using EqualsTest = testing::Test;
+
+} // namespace
+
+TEST_F(EqualsTest, NullStruct) {
+ RectPtr r1;
+ RectPtr r2;
+ EXPECT_TRUE(r1.Equals(r2));
+ EXPECT_TRUE(r2.Equals(r1));
+
+ r1 = CreateRect();
+ EXPECT_FALSE(r1.Equals(r2));
+ EXPECT_FALSE(r2.Equals(r1));
+}
+
+TEST_F(EqualsTest, Struct) {
+ RectPtr r1(CreateRect());
+ RectPtr r2(r1.Clone());
+ EXPECT_TRUE(r1.Equals(r2));
+ r2->y = 1;
+ EXPECT_FALSE(r1.Equals(r2));
+ r2.reset();
+ EXPECT_FALSE(r1.Equals(r2));
+}
+
+TEST_F(EqualsTest, StructNested) {
+ RectPairPtr p1(RectPair::New());
+ p1->first = CreateRect();
+ p1->second = CreateRect();
+ RectPairPtr p2(p1.Clone());
+ EXPECT_TRUE(p1.Equals(p2));
+ p2->second->width = 0;
+ EXPECT_FALSE(p1.Equals(p2));
+ p2->second.reset();
+ EXPECT_FALSE(p1.Equals(p2));
+}
+
+TEST_F(EqualsTest, Array) {
+ NamedRegionPtr n1(NamedRegion::New());
+ n1->name = "n1";
+ n1->rects.push_back(CreateRect());
+ NamedRegionPtr n2(n1.Clone());
+ EXPECT_TRUE(n1.Equals(n2));
+
+ n2->rects.reset();
+ EXPECT_FALSE(n1.Equals(n2));
+ n2->rects.resize(0);
+ EXPECT_FALSE(n1.Equals(n2));
+
+ n2->rects.push_back(CreateRect());
+ n2->rects.push_back(CreateRect());
+ EXPECT_FALSE(n1.Equals(n2));
+
+ n2->rects.resize(1);
+ n2->rects[0]->width = 0;
+ EXPECT_FALSE(n1.Equals(n2));
+
+ n2->rects[0] = CreateRect();
+ EXPECT_TRUE(n1.Equals(n2));
+}
+
+TEST_F(EqualsTest, Map) {
+ auto n1(NamedRegion::New());
+ n1->name = "foo";
+ n1->rects.push_back(CreateRect());
+
+ Map<std::string, NamedRegionPtr> m1;
+ m1.insert("foo", n1.Pass());
+
+ decltype(m1) m2;
+ EXPECT_FALSE(m1.Equals(m2));
+
+ m2.insert("bar", m1.at("foo").Clone());
+ EXPECT_FALSE(m1.Equals(m2));
+
+ m2 = m1.Clone();
+ m2.at("foo")->name = "monkey";
+ EXPECT_FALSE(m1.Equals(m2));
+
+ m2 = m1.Clone();
+ m2.at("foo")->rects.push_back(Rect::New());
+ EXPECT_FALSE(m1.Equals(m2));
+
+ m2.at("foo")->rects.resize(1);
+ m2.at("foo")->rects[0]->width = 1;
+ EXPECT_FALSE(m1.Equals(m2));
+
+ m2 = m1.Clone();
+ EXPECT_TRUE(m1.Equals(m2));
+}
+
+TEST_F(EqualsTest, InterfacePtr) {
+ using InterfaceValueTraits = mojo::internal::ValueTraits<SomeInterfacePtr>;
+
+ SomeInterfacePtr inf1;
+ SomeInterfacePtr inf2;
+
+ EXPECT_TRUE(InterfaceValueTraits::Equals(inf1, inf1));
+ EXPECT_TRUE(InterfaceValueTraits::Equals(inf1, inf2));
+
+ auto inf1_request = GetProxy(&inf1);
+ MOJO_ALLOW_UNUSED_LOCAL(inf1_request);
+
+ EXPECT_TRUE(InterfaceValueTraits::Equals(inf1, inf1));
+ EXPECT_FALSE(InterfaceValueTraits::Equals(inf1, inf2));
+
+ auto inf2_request = GetProxy(&inf2);
+ MOJO_ALLOW_UNUSED_LOCAL(inf2_request);
+
+ EXPECT_FALSE(InterfaceValueTraits::Equals(inf1, inf2));
+}
+
+TEST_F(EqualsTest, InterfaceRequest) {
+ using RequestValueTraits =
+ mojo::internal::ValueTraits<InterfaceRequest<SomeInterface>>;
+
+ InterfaceRequest<SomeInterface> req1;
+ InterfaceRequest<SomeInterface> req2;
+
+ EXPECT_TRUE(RequestValueTraits::Equals(req1, req1));
+ EXPECT_TRUE(RequestValueTraits::Equals(req1, req2));
+
+ SomeInterfacePtr inf1;
+ req1 = GetProxy(&inf1);
+
+ EXPECT_TRUE(RequestValueTraits::Equals(req1, req1));
+ EXPECT_FALSE(RequestValueTraits::Equals(req1, req2));
+
+ SomeInterfacePtr inf2;
+ req2 = GetProxy(&inf2);
+
+ EXPECT_FALSE(RequestValueTraits::Equals(req1, req2));
+}
+
+} // test
+} // mojo
diff --git a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
new file mode 100644
index 0000000..8487d0a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
@@ -0,0 +1,353 @@
+// Copyright 2013 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 "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/interfaces/bindings/tests/sample_factory.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+const char kText1[] = "hello";
+const char kText2[] = "world";
+
+class StringRecorder {
+ public:
+ explicit StringRecorder(std::string* buf) : buf_(buf) {}
+ void Run(const String& a) const { *buf_ = a.To<std::string>(); }
+
+ private:
+ std::string* buf_;
+};
+
+class ImportedInterfaceImpl : public imported::ImportedInterface {
+ public:
+ explicit ImportedInterfaceImpl(
+ InterfaceRequest<imported::ImportedInterface> request)
+ : binding_(this, request.Pass()) {}
+
+ void DoSomething() override { do_something_count_++; }
+
+ static int do_something_count() { return do_something_count_; }
+
+ private:
+ static int do_something_count_;
+ Binding<ImportedInterface> binding_;
+};
+int ImportedInterfaceImpl::do_something_count_ = 0;
+
+class SampleNamedObjectImpl : public sample::NamedObject {
+ public:
+ explicit SampleNamedObjectImpl(InterfaceRequest<sample::NamedObject> request)
+ : binding_(this, request.Pass()) {}
+ void SetName(const mojo::String& name) override { name_ = name; }
+
+ void GetName(const mojo::Callback<void(mojo::String)>& callback) override {
+ callback.Run(name_);
+ }
+
+ private:
+ std::string name_;
+ StrongBinding<sample::NamedObject> binding_;
+};
+
+class SampleFactoryImpl : public sample::Factory {
+ public:
+ explicit SampleFactoryImpl(InterfaceRequest<sample::Factory> request)
+ : binding_(this, request.Pass()) {}
+
+ void DoStuff(sample::RequestPtr request,
+ ScopedMessagePipeHandle pipe,
+ const DoStuffCallback& callback) override {
+ std::string text1;
+ if (pipe.is_valid())
+ EXPECT_TRUE(ReadTextMessage(pipe.get(), &text1));
+
+ std::string text2;
+ if (request->pipe.is_valid()) {
+ EXPECT_TRUE(ReadTextMessage(request->pipe.get(), &text2));
+
+ // Ensure that simply accessing request->pipe does not close it.
+ EXPECT_TRUE(request->pipe.is_valid());
+ }
+
+ ScopedMessagePipeHandle pipe0;
+ if (!text2.empty()) {
+ CreateMessagePipe(nullptr, &pipe0, &pipe1_);
+ EXPECT_TRUE(WriteTextMessage(pipe1_.get(), text2));
+ }
+
+ sample::ResponsePtr response(sample::Response::New());
+ response->x = 2;
+ response->pipe = pipe0.Pass();
+ callback.Run(response.Pass(), text1);
+
+ if (request->obj)
+ request->obj->DoSomething();
+ }
+
+ void DoStuff2(ScopedDataPipeConsumerHandle pipe,
+ const DoStuff2Callback& callback) override {
+ // Read the data from the pipe, writing the response (as a string) to
+ // DidStuff2().
+ ASSERT_TRUE(pipe.is_valid());
+ uint32_t data_size = 0;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ ReadDataRaw(
+ pipe.get(), nullptr, &data_size, MOJO_READ_DATA_FLAG_QUERY));
+ ASSERT_NE(0, static_cast<int>(data_size));
+ char data[64];
+ ASSERT_LT(static_cast<int>(data_size), 64);
+ ASSERT_EQ(
+ MOJO_RESULT_OK,
+ ReadDataRaw(
+ pipe.get(), data, &data_size, MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+ callback.Run(data);
+ }
+
+ void CreateNamedObject(
+ InterfaceRequest<sample::NamedObject> object_request) override {
+ EXPECT_TRUE(object_request.is_pending());
+ new SampleNamedObjectImpl(object_request.Pass());
+ }
+
+ // These aren't called or implemented, but exist here to test that the
+ // methods are generated with the correct argument types for imported
+ // interfaces.
+ void RequestImportedInterface(
+ InterfaceRequest<imported::ImportedInterface> imported,
+ const mojo::Callback<void(InterfaceRequest<imported::ImportedInterface>)>&
+ callback) override {}
+ void TakeImportedInterface(
+ imported::ImportedInterfacePtr imported,
+ const mojo::Callback<void(imported::ImportedInterfacePtr)>& callback)
+ override {}
+
+ private:
+ ScopedMessagePipeHandle pipe1_;
+ Binding<sample::Factory> binding_;
+};
+
+class HandlePassingTest : public testing::Test {
+ public:
+ HandlePassingTest() : loop_(common::MessagePumpMojo::Create()) {}
+
+ void TearDown() override { PumpMessages(); }
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ private:
+ base::MessageLoop loop_;
+};
+
+struct DoStuffCallback {
+ DoStuffCallback(bool* got_response, std::string* got_text_reply)
+ : got_response(got_response), got_text_reply(got_text_reply) {}
+
+ void Run(sample::ResponsePtr response, const String& text_reply) const {
+ *got_text_reply = text_reply;
+
+ if (response->pipe.is_valid()) {
+ std::string text2;
+ EXPECT_TRUE(ReadTextMessage(response->pipe.get(), &text2));
+
+ // Ensure that simply accessing response.pipe does not close it.
+ EXPECT_TRUE(response->pipe.is_valid());
+
+ EXPECT_EQ(std::string(kText2), text2);
+
+ // Do some more tests of handle passing:
+ ScopedMessagePipeHandle p = response->pipe.Pass();
+ EXPECT_TRUE(p.is_valid());
+ EXPECT_FALSE(response->pipe.is_valid());
+ }
+
+ *got_response = true;
+ }
+
+ bool* got_response;
+ std::string* got_text_reply;
+};
+
+TEST_F(HandlePassingTest, Basic) {
+ sample::FactoryPtr factory;
+ SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+ MessagePipe pipe0;
+ EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1));
+
+ MessagePipe pipe1;
+ EXPECT_TRUE(WriteTextMessage(pipe1.handle1.get(), kText2));
+
+ imported::ImportedInterfacePtr imported;
+ ImportedInterfaceImpl imported_impl(GetProxy(&imported));
+
+ sample::RequestPtr request(sample::Request::New());
+ request->x = 1;
+ request->pipe = pipe1.handle0.Pass();
+ request->obj = imported.Pass();
+ bool got_response = false;
+ std::string got_text_reply;
+ DoStuffCallback cb(&got_response, &got_text_reply);
+ factory->DoStuff(request.Pass(), pipe0.handle0.Pass(), cb);
+
+ EXPECT_FALSE(*cb.got_response);
+ int count_before = ImportedInterfaceImpl::do_something_count();
+
+ PumpMessages();
+
+ EXPECT_TRUE(*cb.got_response);
+ EXPECT_EQ(kText1, *cb.got_text_reply);
+ EXPECT_EQ(1, ImportedInterfaceImpl::do_something_count() - count_before);
+}
+
+TEST_F(HandlePassingTest, PassInvalid) {
+ sample::FactoryPtr factory;
+ SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+ sample::RequestPtr request(sample::Request::New());
+ request->x = 1;
+ bool got_response = false;
+ std::string got_text_reply;
+ DoStuffCallback cb(&got_response, &got_text_reply);
+ factory->DoStuff(request.Pass(), ScopedMessagePipeHandle().Pass(), cb);
+
+ EXPECT_FALSE(*cb.got_response);
+
+ PumpMessages();
+
+ EXPECT_TRUE(*cb.got_response);
+}
+
+struct DoStuff2Callback {
+ DoStuff2Callback(bool* got_response, std::string* got_text_reply)
+ : got_response(got_response), got_text_reply(got_text_reply) {}
+
+ void Run(const String& text_reply) const {
+ *got_response = true;
+ *got_text_reply = text_reply;
+ }
+
+ bool* got_response;
+ std::string* got_text_reply;
+};
+
+// Verifies DataPipeConsumer can be passed and read from.
+TEST_F(HandlePassingTest, DataPipe) {
+ sample::FactoryPtr factory;
+ SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+ // Writes a string to a data pipe and passes the data pipe (consumer) to the
+ // factory.
+ ScopedDataPipeProducerHandle producer_handle;
+ ScopedDataPipeConsumerHandle consumer_handle;
+ MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions),
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ 1,
+ 1024};
+ ASSERT_EQ(MOJO_RESULT_OK,
+ CreateDataPipe(&options, &producer_handle, &consumer_handle));
+ std::string expected_text_reply = "got it";
+ // +1 for \0.
+ uint32_t data_size = static_cast<uint32_t>(expected_text_reply.size() + 1);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ WriteDataRaw(producer_handle.get(),
+ expected_text_reply.c_str(),
+ &data_size,
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+
+ bool got_response = false;
+ std::string got_text_reply;
+ DoStuff2Callback cb(&got_response, &got_text_reply);
+ factory->DoStuff2(consumer_handle.Pass(), cb);
+
+ EXPECT_FALSE(*cb.got_response);
+
+ PumpMessages();
+
+ EXPECT_TRUE(*cb.got_response);
+ EXPECT_EQ(expected_text_reply, *cb.got_text_reply);
+}
+
+TEST_F(HandlePassingTest, PipesAreClosed) {
+ sample::FactoryPtr factory;
+ SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+ MessagePipe extra_pipe;
+
+ MojoHandle handle0_value = extra_pipe.handle0.get().value();
+ MojoHandle handle1_value = extra_pipe.handle1.get().value();
+
+ {
+ Array<ScopedMessagePipeHandle> pipes(2);
+ pipes[0] = extra_pipe.handle0.Pass();
+ pipes[1] = extra_pipe.handle1.Pass();
+
+ sample::RequestPtr request(sample::Request::New());
+ request->more_pipes = pipes.Pass();
+
+ factory->DoStuff(request.Pass(), ScopedMessagePipeHandle(),
+ sample::Factory::DoStuffCallback());
+ }
+
+ // We expect the pipes to have been closed.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handle0_value));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handle1_value));
+}
+
+TEST_F(HandlePassingTest, IsHandle) {
+ // Validate that mojo::internal::IsHandle<> works as expected since this.
+ // template is key to ensuring that we don't leak handles.
+ EXPECT_TRUE(internal::IsHandle<Handle>::value);
+ EXPECT_TRUE(internal::IsHandle<MessagePipeHandle>::value);
+ EXPECT_TRUE(internal::IsHandle<DataPipeConsumerHandle>::value);
+ EXPECT_TRUE(internal::IsHandle<DataPipeProducerHandle>::value);
+ EXPECT_TRUE(internal::IsHandle<SharedBufferHandle>::value);
+
+ // Basic sanity checks...
+ EXPECT_FALSE(internal::IsHandle<int>::value);
+ EXPECT_FALSE(internal::IsHandle<sample::FactoryPtr>::value);
+ EXPECT_FALSE(internal::IsHandle<String>::value);
+}
+
+TEST_F(HandlePassingTest, CreateNamedObject) {
+ sample::FactoryPtr factory;
+ SampleFactoryImpl factory_impl(GetProxy(&factory));
+
+ sample::NamedObjectPtr object1;
+ EXPECT_FALSE(object1);
+
+ InterfaceRequest<sample::NamedObject> object1_request = GetProxy(&object1);
+ EXPECT_TRUE(object1_request.is_pending());
+ factory->CreateNamedObject(object1_request.Pass());
+ EXPECT_FALSE(object1_request.is_pending()); // We've passed the request.
+
+ ASSERT_TRUE(object1);
+ object1->SetName("object1");
+
+ sample::NamedObjectPtr object2;
+ factory->CreateNamedObject(GetProxy(&object2));
+ object2->SetName("object2");
+
+ std::string name1;
+ object1->GetName(StringRecorder(&name1));
+
+ std::string name2;
+ object2->GetName(StringRecorder(&name2));
+
+ PumpMessages(); // Yield for results.
+
+ EXPECT_EQ(std::string("object1"), name1);
+ EXPECT_EQ(std::string("object2"), name2);
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
new file mode 100644
index 0000000..508787a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
@@ -0,0 +1,647 @@
+// Copyright 2013 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 "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/scoping.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+template <typename Method, typename Class>
+class RunnableImpl {
+ public:
+ RunnableImpl(Method method, Class instance)
+ : method_(method), instance_(instance) {}
+ template <typename... Args>
+ void Run(Args... args) const {
+ (instance_->*method_)(args...);
+ }
+
+ private:
+ Method method_;
+ Class instance_;
+};
+
+template <typename Method, typename Class>
+RunnableImpl<Method, Class> MakeRunnable(Method method, Class object) {
+ return RunnableImpl<Method, Class>(method, object);
+}
+
+typedef mojo::Callback<void(double)> CalcCallback;
+
+class MathCalculatorImpl : public math::Calculator {
+ public:
+ explicit MathCalculatorImpl(InterfaceRequest<math::Calculator> request)
+ : total_(0.0), binding_(this, request.Pass()) {}
+ ~MathCalculatorImpl() override {}
+
+ void CloseMessagePipe() { binding_.Close(); }
+
+ void WaitForIncomingMethodCall() { binding_.WaitForIncomingMethodCall(); }
+
+ void Clear(const CalcCallback& callback) override {
+ total_ = 0.0;
+ callback.Run(total_);
+ }
+
+ void Add(double value, const CalcCallback& callback) override {
+ total_ += value;
+ callback.Run(total_);
+ }
+
+ void Multiply(double value, const CalcCallback& callback) override {
+ total_ *= value;
+ callback.Run(total_);
+ }
+
+ private:
+ double total_;
+ Binding<math::Calculator> binding_;
+};
+
+class MathCalculatorUI {
+ public:
+ explicit MathCalculatorUI(math::CalculatorPtr calculator)
+ : calculator_(calculator.Pass()),
+ output_(0.0),
+ callback_(MakeRunnable(&MathCalculatorUI::Output, this)) {}
+
+ bool WaitForIncomingResponse() {
+ return calculator_.WaitForIncomingResponse();
+ }
+
+ bool encountered_error() const { return calculator_.encountered_error(); }
+
+ void Add(double value) { calculator_->Add(value, callback_); }
+
+ void Subtract(double value) { calculator_->Add(-value, callback_); }
+
+ void Multiply(double value) { calculator_->Multiply(value, callback_); }
+
+ void Divide(double value) { calculator_->Multiply(1.0 / value, callback_); }
+
+ double GetOutput() const { return output_; }
+
+ private:
+ void Output(double output) { output_ = output; }
+
+ math::CalculatorPtr calculator_;
+ double output_;
+ Callback<void(double)> callback_;
+};
+
+class SelfDestructingMathCalculatorUI {
+ public:
+ explicit SelfDestructingMathCalculatorUI(math::CalculatorPtr calculator)
+ : calculator_(calculator.Pass()), nesting_level_(0) {
+ ++num_instances_;
+ }
+
+ void BeginTest(bool nested) {
+ nesting_level_ = nested ? 2 : 1;
+ calculator_->Add(
+ 1.0, MakeRunnable(&SelfDestructingMathCalculatorUI::Output, this));
+ }
+
+ static int num_instances() { return num_instances_; }
+
+ void Output(double value) {
+ if (--nesting_level_ > 0) {
+ // Add some more and wait for re-entrant call to Output!
+ calculator_->Add(
+ 1.0, MakeRunnable(&SelfDestructingMathCalculatorUI::Output, this));
+ base::MessageLoop::current()->RunUntilIdle();
+ } else {
+ delete this;
+ }
+ }
+
+ private:
+ ~SelfDestructingMathCalculatorUI() { --num_instances_; }
+
+ math::CalculatorPtr calculator_;
+ int nesting_level_;
+ static int num_instances_;
+};
+
+// static
+int SelfDestructingMathCalculatorUI::num_instances_ = 0;
+
+class ReentrantServiceImpl : public sample::Service {
+ public:
+ ~ReentrantServiceImpl() override {}
+
+ explicit ReentrantServiceImpl(InterfaceRequest<sample::Service> request)
+ : call_depth_(0), max_call_depth_(0), binding_(this, request.Pass()) {}
+
+ int max_call_depth() { return max_call_depth_; }
+
+ void Frobinate(sample::FooPtr foo,
+ sample::Service::BazOptions baz,
+ sample::PortPtr port,
+ const sample::Service::FrobinateCallback& callback) override {
+ max_call_depth_ = std::max(++call_depth_, max_call_depth_);
+ if (call_depth_ == 1) {
+ EXPECT_TRUE(binding_.WaitForIncomingMethodCall());
+ }
+ call_depth_--;
+ callback.Run(5);
+ }
+
+ void GetPort(mojo::InterfaceRequest<sample::Port> port) override {}
+
+ private:
+ int call_depth_;
+ int max_call_depth_;
+ Binding<sample::Service> binding_;
+};
+
+class IntegerAccessorImpl : public sample::IntegerAccessor {
+ public:
+ IntegerAccessorImpl() : integer_(0) {}
+ ~IntegerAccessorImpl() override {}
+
+ int64_t integer() const { return integer_; }
+
+ private:
+ // sample::IntegerAccessor implementation.
+ void GetInteger(const GetIntegerCallback& callback) override {
+ callback.Run(integer_, sample::ENUM_VALUE);
+ }
+ void SetInteger(int64_t data, sample::Enum type) override { integer_ = data; }
+
+ int64_t integer_;
+};
+
+class InterfacePtrTest : public testing::Test {
+ public:
+ InterfacePtrTest() : loop_(common::MessagePumpMojo::Create()) {}
+ ~InterfacePtrTest() override { loop_.RunUntilIdle(); }
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ private:
+ base::MessageLoop loop_;
+};
+
+TEST_F(InterfacePtrTest, IsBound) {
+ math::CalculatorPtr calc;
+ EXPECT_FALSE(calc.is_bound());
+ MathCalculatorImpl calc_impl(GetProxy(&calc));
+ EXPECT_TRUE(calc.is_bound());
+}
+
+TEST_F(InterfacePtrTest, EndToEnd) {
+ math::CalculatorPtr calc;
+ MathCalculatorImpl calc_impl(GetProxy(&calc));
+
+ // Suppose this is instantiated in a process that has pipe1_.
+ MathCalculatorUI calculator_ui(calc.Pass());
+
+ calculator_ui.Add(2.0);
+ calculator_ui.Multiply(5.0);
+
+ PumpMessages();
+
+ EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
+ math::CalculatorPtr calc;
+ MathCalculatorImpl calc_impl(GetProxy(&calc));
+
+ // Suppose this is instantiated in a process that has pipe1_.
+ MathCalculatorUI calculator_ui(calc.Pass());
+
+ EXPECT_EQ(0.0, calculator_ui.GetOutput());
+
+ calculator_ui.Add(2.0);
+ EXPECT_EQ(0.0, calculator_ui.GetOutput());
+ calc_impl.WaitForIncomingMethodCall();
+ calculator_ui.WaitForIncomingResponse();
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+
+ calculator_ui.Multiply(5.0);
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+ calc_impl.WaitForIncomingMethodCall();
+ calculator_ui.WaitForIncomingResponse();
+ EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, Movable) {
+ math::CalculatorPtr a;
+ math::CalculatorPtr b;
+ MathCalculatorImpl calc_impl(GetProxy(&b));
+
+ EXPECT_TRUE(!a);
+ EXPECT_FALSE(!b);
+
+ a = b.Pass();
+
+ EXPECT_FALSE(!a);
+ EXPECT_TRUE(!b);
+}
+
+TEST_F(InterfacePtrTest, Resettable) {
+ math::CalculatorPtr a;
+
+ EXPECT_TRUE(!a);
+
+ MessagePipe pipe;
+
+ // Save this so we can test it later.
+ Handle handle = pipe.handle0.get();
+
+ a = MakeProxy(InterfacePtrInfo<math::Calculator>(pipe.handle0.Pass(), 0u));
+
+ EXPECT_FALSE(!a);
+
+ a.reset();
+
+ EXPECT_TRUE(!a);
+ EXPECT_FALSE(a.internal_state()->router_for_testing());
+
+ // Test that handle was closed.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle));
+}
+
+TEST_F(InterfacePtrTest, BindInvalidHandle) {
+ math::CalculatorPtr ptr;
+ EXPECT_FALSE(ptr.get());
+ EXPECT_FALSE(ptr);
+
+ ptr.Bind(InterfacePtrInfo<math::Calculator>());
+ EXPECT_FALSE(ptr.get());
+ EXPECT_FALSE(ptr);
+}
+
+TEST_F(InterfacePtrTest, EncounteredError) {
+ math::CalculatorPtr proxy;
+ MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+ MathCalculatorUI calculator_ui(proxy.Pass());
+
+ calculator_ui.Add(2.0);
+ PumpMessages();
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+ EXPECT_FALSE(calculator_ui.encountered_error());
+
+ calculator_ui.Multiply(5.0);
+ EXPECT_FALSE(calculator_ui.encountered_error());
+
+ // Close the server.
+ calc_impl.CloseMessagePipe();
+
+ // The state change isn't picked up locally yet.
+ EXPECT_FALSE(calculator_ui.encountered_error());
+
+ PumpMessages();
+
+ // OK, now we see the error.
+ EXPECT_TRUE(calculator_ui.encountered_error());
+}
+
+TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
+ math::CalculatorPtr proxy;
+ MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+ bool encountered_error = false;
+ proxy.set_connection_error_handler(
+ [&encountered_error]() { encountered_error = true; });
+
+ MathCalculatorUI calculator_ui(proxy.Pass());
+
+ calculator_ui.Add(2.0);
+ PumpMessages();
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+ EXPECT_FALSE(calculator_ui.encountered_error());
+
+ calculator_ui.Multiply(5.0);
+ EXPECT_FALSE(calculator_ui.encountered_error());
+
+ // Close the server.
+ calc_impl.CloseMessagePipe();
+
+ // The state change isn't picked up locally yet.
+ EXPECT_FALSE(calculator_ui.encountered_error());
+
+ PumpMessages();
+
+ // OK, now we see the error.
+ EXPECT_TRUE(calculator_ui.encountered_error());
+
+ // We should have also been able to observe the error through the error
+ // handler.
+ EXPECT_TRUE(encountered_error);
+}
+
+TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) {
+ math::CalculatorPtr proxy;
+ MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+
+ SelfDestructingMathCalculatorUI* impl =
+ new SelfDestructingMathCalculatorUI(proxy.Pass());
+ impl->BeginTest(false);
+
+ PumpMessages();
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+}
+
+TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) {
+ math::CalculatorPtr proxy;
+ MathCalculatorImpl calc_impl(GetProxy(&proxy));
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+
+ SelfDestructingMathCalculatorUI* impl =
+ new SelfDestructingMathCalculatorUI(proxy.Pass());
+ impl->BeginTest(true);
+
+ PumpMessages();
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
+}
+
+TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
+ sample::ServicePtr proxy;
+ ReentrantServiceImpl impl(GetProxy(&proxy));
+
+ proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ sample::Service::FrobinateCallback());
+ proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
+ sample::Service::FrobinateCallback());
+
+ PumpMessages();
+
+ EXPECT_EQ(2, impl.max_call_depth());
+}
+
+TEST_F(InterfacePtrTest, QueryVersion) {
+ IntegerAccessorImpl impl;
+ sample::IntegerAccessorPtr ptr;
+ Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
+
+ EXPECT_EQ(0u, ptr.version());
+
+ auto callback = [](uint32_t version) { EXPECT_EQ(3u, version); };
+ ptr.QueryVersion(callback);
+
+ PumpMessages();
+
+ EXPECT_EQ(3u, ptr.version());
+}
+
+TEST_F(InterfacePtrTest, RequireVersion) {
+ IntegerAccessorImpl impl;
+ sample::IntegerAccessorPtr ptr;
+ Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
+
+ EXPECT_EQ(0u, ptr.version());
+
+ ptr.RequireVersion(1u);
+ EXPECT_EQ(1u, ptr.version());
+ ptr->SetInteger(123, sample::ENUM_VALUE);
+ PumpMessages();
+ EXPECT_FALSE(ptr.encountered_error());
+ EXPECT_EQ(123, impl.integer());
+
+ ptr.RequireVersion(3u);
+ EXPECT_EQ(3u, ptr.version());
+ ptr->SetInteger(456, sample::ENUM_VALUE);
+ PumpMessages();
+ EXPECT_FALSE(ptr.encountered_error());
+ EXPECT_EQ(456, impl.integer());
+
+ // Require a version that is not supported by the impl side.
+ ptr.RequireVersion(4u);
+ // This value is set to the input of RequireVersion() synchronously.
+ EXPECT_EQ(4u, ptr.version());
+ ptr->SetInteger(789, sample::ENUM_VALUE);
+ PumpMessages();
+ EXPECT_TRUE(ptr.encountered_error());
+ // The call to SetInteger() after RequireVersion(4u) is ignored.
+ EXPECT_EQ(456, impl.integer());
+}
+
+class StrongMathCalculatorImpl : public math::Calculator {
+ public:
+ StrongMathCalculatorImpl(ScopedMessagePipeHandle handle,
+ bool* error_received,
+ bool* destroyed)
+ : error_received_(error_received),
+ destroyed_(destroyed),
+ binding_(this, handle.Pass()) {
+ binding_.set_connection_error_handler(
+ [this]() { *error_received_ = true; });
+ }
+ ~StrongMathCalculatorImpl() override { *destroyed_ = true; }
+
+ // math::Calculator implementation.
+ void Clear(const CalcCallback& callback) override { callback.Run(total_); }
+
+ void Add(double value, const CalcCallback& callback) override {
+ total_ += value;
+ callback.Run(total_);
+ }
+
+ void Multiply(double value, const CalcCallback& callback) override {
+ total_ *= value;
+ callback.Run(total_);
+ }
+
+ private:
+ double total_ = 0.0;
+ bool* error_received_;
+ bool* destroyed_;
+
+ StrongBinding<math::Calculator> binding_;
+};
+
+TEST(StrongConnectorTest, Math) {
+ base::MessageLoop loop(common::MessagePumpMojo::Create());
+
+ bool error_received = false;
+ bool destroyed = false;
+ MessagePipe pipe;
+ new StrongMathCalculatorImpl(pipe.handle0.Pass(), &error_received,
+ &destroyed);
+
+ math::CalculatorPtr calc;
+ calc.Bind(InterfacePtrInfo<math::Calculator>(pipe.handle1.Pass(), 0u));
+
+ {
+ // Suppose this is instantiated in a process that has the other end of the
+ // message pipe.
+ MathCalculatorUI calculator_ui(calc.Pass());
+
+ calculator_ui.Add(2.0);
+ calculator_ui.Multiply(5.0);
+
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(10.0, calculator_ui.GetOutput());
+ EXPECT_FALSE(error_received);
+ EXPECT_FALSE(destroyed);
+ }
+ // Destroying calculator_ui should close the pipe and generate an error on the
+ // other
+ // end which will destroy the instance since it is strongly bound.
+
+ loop.RunUntilIdle();
+ EXPECT_TRUE(error_received);
+ EXPECT_TRUE(destroyed);
+}
+
+class WeakMathCalculatorImpl : public math::Calculator {
+ public:
+ WeakMathCalculatorImpl(ScopedMessagePipeHandle handle,
+ bool* error_received,
+ bool* destroyed)
+ : error_received_(error_received),
+ destroyed_(destroyed),
+ binding_(this, handle.Pass()) {
+ binding_.set_connection_error_handler(
+ [this]() { *error_received_ = true; });
+ }
+ ~WeakMathCalculatorImpl() override { *destroyed_ = true; }
+
+ void Clear(const CalcCallback& callback) override { callback.Run(total_); }
+
+ void Add(double value, const CalcCallback& callback) override {
+ total_ += value;
+ callback.Run(total_);
+ }
+
+ void Multiply(double value, const CalcCallback& callback) override {
+ total_ *= value;
+ callback.Run(total_);
+ }
+
+ private:
+ double total_ = 0.0;
+ bool* error_received_;
+ bool* destroyed_;
+
+ Binding<math::Calculator> binding_;
+};
+
+TEST(WeakConnectorTest, Math) {
+ base::MessageLoop loop(common::MessagePumpMojo::Create());
+
+ bool error_received = false;
+ bool destroyed = false;
+ MessagePipe pipe;
+ WeakMathCalculatorImpl impl(pipe.handle0.Pass(), &error_received, &destroyed);
+
+ math::CalculatorPtr calc;
+ calc.Bind(InterfacePtrInfo<math::Calculator>(pipe.handle1.Pass(), 0u));
+
+ {
+ // Suppose this is instantiated in a process that has the other end of the
+ // message pipe.
+ MathCalculatorUI calculator_ui(calc.Pass());
+
+ calculator_ui.Add(2.0);
+ calculator_ui.Multiply(5.0);
+
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(10.0, calculator_ui.GetOutput());
+ EXPECT_FALSE(error_received);
+ EXPECT_FALSE(destroyed);
+ // Destroying calculator_ui should close the pipe and generate an error on
+ // the other
+ // end which will destroy the instance since it is strongly bound.
+ }
+
+ loop.RunUntilIdle();
+ EXPECT_TRUE(error_received);
+ EXPECT_FALSE(destroyed);
+}
+
+class CImpl : public C {
+ public:
+ CImpl(bool* d_called, InterfaceRequest<C> request)
+ : d_called_(d_called),
+ binding_(this, request.Pass()) {}
+ ~CImpl() override {}
+
+ private:
+ void D() override {
+ *d_called_ = true;
+ }
+
+ bool* d_called_;
+ StrongBinding<C> binding_;
+};
+
+class BImpl : public B {
+ public:
+ BImpl(bool* d_called, InterfaceRequest<B> request)
+ : d_called_(d_called),
+ binding_(this, request.Pass()) {}
+ ~BImpl() override {}
+
+ private:
+ void GetC(InterfaceRequest<C> c) override {
+ new CImpl(d_called_, c.Pass());
+ }
+
+ bool* d_called_;
+ StrongBinding<B> binding_;
+};
+
+class AImpl : public A {
+ public:
+ explicit AImpl(InterfaceRequest<A> request)
+ : d_called_(false),
+ binding_(this, request.Pass()) {}
+ ~AImpl() override {}
+
+ bool d_called() const { return d_called_; }
+
+ private:
+ void GetB(InterfaceRequest<B> b) override {
+ new BImpl(&d_called_, b.Pass());
+ }
+
+ bool d_called_;
+ Binding<A> binding_;
+};
+
+TEST_F(InterfacePtrTest, Scoping) {
+ APtr a;
+ AImpl a_impl(GetProxy(&a));
+
+ EXPECT_FALSE(a_impl.d_called());
+
+ {
+ BPtr b;
+ a->GetB(GetProxy(&b));
+ CPtr c;
+ b->GetC(GetProxy(&c));
+ c->D();
+ }
+
+ // While B & C have fallen out of scope, the pipes will remain until they are
+ // flushed.
+ EXPECT_FALSE(a_impl.d_called());
+ PumpMessages();
+ EXPECT_TRUE(a_impl.d_called());
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/map_unittest.cc b/mojo/public/cpp/bindings/tests/map_unittest.cc
new file mode 100644
index 0000000..5b049a8
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/map_unittest.cc
@@ -0,0 +1,316 @@
+// Copyright 2014 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/array_serialization.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/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 mojo {
+namespace test {
+
+namespace {
+
+using mojo::internal::Array_Data;
+using mojo::internal::ArrayValidateParams;
+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 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] = array.Pass();
+ 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.GetValue().size());
+ EXPECT_EQ(kStringIntData[i].int_data, it.GetValue()[0]);
+ }
+}
+
+TEST_F(MapTest, ConstructedFromArray) {
+ Array<String> keys(kStringIntDataSize);
+ Array<int> values(kStringIntDataSize);
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ keys[i] = kStringIntData[i].string_data;
+ values[i] = kStringIntData[i].int_data;
+ }
+
+ Map<String, int> map(keys.Pass(), values.Pass());
+
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ EXPECT_EQ(kStringIntData[i].int_data,
+ map.at(mojo::String(kStringIntData[i].string_data)));
+ }
+}
+
+TEST_F(MapTest, DecomposeMapTo) {
+ Array<String> keys(kStringIntDataSize);
+ Array<int> values(kStringIntDataSize);
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ keys[i] = kStringIntData[i].string_data;
+ values[i] = kStringIntData[i].int_data;
+ }
+
+ Map<String, int> map(keys.Pass(), values.Pass());
+ EXPECT_EQ(kStringIntDataSize, map.size());
+
+ Array<String> keys2;
+ Array<int> values2;
+ map.DecomposeMapTo(&keys2, &values2);
+ EXPECT_EQ(0u, map.size());
+
+ EXPECT_EQ(kStringIntDataSize, keys2.size());
+ EXPECT_EQ(kStringIntDataSize, values2.size());
+
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ // We are not guaranteed that the copies have the same sorting as the
+ // originals.
+ String key = kStringIntData[i].string_data;
+ int value = kStringIntData[i].int_data;
+
+ bool found = false;
+ for (size_t j = 0; j < keys2.size(); ++j) {
+ if (keys2[j] == key) {
+ EXPECT_EQ(value, values2[j]);
+ found = true;
+ break;
+ }
+ }
+
+ EXPECT_TRUE(found);
+ }
+}
+
+TEST_F(MapTest, Insert_Copyable) {
+ ASSERT_EQ(0u, CopyableType::num_instances());
+ mojo::Map<mojo::String, CopyableType> map;
+ std::vector<CopyableType*> value_ptrs;
+
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ const char* key = kStringIntData[i].string_data;
+ CopyableType value;
+ value_ptrs.push_back(value.ptr());
+ map.insert(key, value);
+ ASSERT_EQ(i + 1, map.size());
+ ASSERT_EQ(i + 1, value_ptrs.size());
+ EXPECT_EQ(map.size() + 1, CopyableType::num_instances());
+ EXPECT_TRUE(map.at(key).copied());
+ EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
+ map.at(key).ResetCopied();
+ EXPECT_TRUE(map);
+ }
+
+ // std::map doesn't have a capacity() method like std::vector so this test is
+ // a lot more boring.
+
+ map.reset();
+ EXPECT_EQ(0u, CopyableType::num_instances());
+}
+
+TEST_F(MapTest, Insert_MoveOnly) {
+ ASSERT_EQ(0u, MoveOnlyType::num_instances());
+ mojo::Map<mojo::String, MoveOnlyType> map;
+ std::vector<MoveOnlyType*> value_ptrs;
+
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ const char* key = kStringIntData[i].string_data;
+ MoveOnlyType value;
+ value_ptrs.push_back(value.ptr());
+ map.insert(key, value.Pass());
+ ASSERT_EQ(i + 1, map.size());
+ ASSERT_EQ(i + 1, value_ptrs.size());
+ EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
+ EXPECT_TRUE(map.at(key).moved());
+ EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
+ map.at(key).ResetMoved();
+ EXPECT_TRUE(map);
+ }
+
+ // std::map doesn't have a capacity() method like std::vector so this test is
+ // a lot more boring.
+
+ map.reset();
+ EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+TEST_F(MapTest, IndexOperator_MoveOnly) {
+ ASSERT_EQ(0u, MoveOnlyType::num_instances());
+ mojo::Map<mojo::String, MoveOnlyType> map;
+ std::vector<MoveOnlyType*> value_ptrs;
+
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ const char* key = kStringIntData[i].string_data;
+ MoveOnlyType value;
+ value_ptrs.push_back(value.ptr());
+ map[key] = value.Pass();
+ ASSERT_EQ(i + 1, map.size());
+ ASSERT_EQ(i + 1, value_ptrs.size());
+ EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
+ EXPECT_TRUE(map.at(key).moved());
+ EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
+ map.at(key).ResetMoved();
+ EXPECT_TRUE(map);
+ }
+
+ // std::map doesn't have a capacity() method like std::vector so this test is
+ // a lot more boring.
+
+ map.reset();
+ EXPECT_EQ(0u, MoveOnlyType::num_instances());
+}
+
+TEST_F(MapTest, STLToMojo) {
+ std::map<std::string, int> stl_data;
+ for (size_t i = 0; i < kStringIntDataSize; ++i)
+ stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data;
+
+ Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data);
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ EXPECT_EQ(kStringIntData[i].int_data,
+ mojo_data.at(kStringIntData[i].string_data));
+ }
+}
+
+TEST_F(MapTest, MojoToSTL) {
+ Map<String, int32_t> mojo_map;
+ for (size_t i = 0; i < kStringIntDataSize; ++i)
+ mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
+
+ std::map<std::string, int> stl_map =
+ mojo_map.To<std::map<std::string, int>>();
+ for (size_t i = 0; i < kStringIntDataSize; ++i) {
+ auto it = stl_map.find(kStringIntData[i].string_data);
+ ASSERT_TRUE(it != stl_map.end());
+ EXPECT_EQ(kStringIntData[i].int_data, it->second);
+ }
+}
+
+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, s.Pass());
+ }
+
+ Map<String, Array<String>> m2 = m.Clone();
+
+ for (auto it = m2.begin(); it != m2.end(); ++it) {
+ ASSERT_EQ(1u, it.GetValue().size());
+ EXPECT_EQ(it.GetKey(), it.GetValue().at(0));
+ }
+}
+
+TEST_F(MapTest, ArrayOfMap) {
+ {
+ Array<Map<int32_t, int8_t>> array(1);
+ array[0].insert(1, 42);
+
+ size_t size = GetSerializedSize_(array);
+ FixedBufferForTesting buf(size);
+ Array_Data<Map_Data<int32_t, int8_t>*>* data;
+ ArrayValidateParams validate_params(
+ 0, false, new ArrayValidateParams(0, false, nullptr));
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<Map<int32_t, int8_t>> deserialized_array;
+ Deserialize_(data, &deserialized_array);
+
+ 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", map_value.Pass());
+
+ size_t size = GetSerializedSize_(array);
+ FixedBufferForTesting buf(size);
+ Array_Data<Map_Data<String_Data*, Array_Data<bool>*>*>* data;
+ ArrayValidateParams validate_params(
+ 0, false, new ArrayValidateParams(
+ 0, false, new ArrayValidateParams(0, false, nullptr)));
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<Map<String, Array<bool>>> deserialized_array;
+ Deserialize_(data, &deserialized_array);
+
+ 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]);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/message_queue.cc b/mojo/public/cpp/bindings/tests/message_queue.cc
new file mode 100644
index 0000000..71cb4905
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/message_queue.cc
@@ -0,0 +1,43 @@
+// Copyright 2013 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/tests/message_queue.h"
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace test {
+
+MessageQueue::MessageQueue() {
+}
+
+MessageQueue::~MessageQueue() {
+ while (!queue_.empty())
+ Pop();
+}
+
+bool MessageQueue::IsEmpty() const {
+ return queue_.empty();
+}
+
+void MessageQueue::Push(Message* message) {
+ queue_.push(new Message());
+ message->MoveTo(queue_.back());
+}
+
+void MessageQueue::Pop(Message* message) {
+ MOJO_DCHECK(!queue_.empty());
+ queue_.front()->MoveTo(message);
+ Pop();
+}
+
+void MessageQueue::Pop() {
+ MOJO_DCHECK(!queue_.empty());
+ delete queue_.front();
+ queue_.pop();
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/message_queue.h b/mojo/public/cpp/bindings/tests/message_queue.h
new file mode 100644
index 0000000..c3091db
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/message_queue.h
@@ -0,0 +1,43 @@
+// Copyright 2013 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_TESTS_MESSAGE_QUEUE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_
+
+#include <queue>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class Message;
+
+namespace test {
+
+// A queue for Message objects.
+class MessageQueue {
+ public:
+ MessageQueue();
+ ~MessageQueue();
+
+ bool IsEmpty() const;
+
+ // This method copies the message data and steals ownership of its handles.
+ void Push(Message* message);
+
+ // Removes the next message from the queue, copying its data and transferring
+ // ownership of its handles to the given |message|.
+ void Pop(Message* message);
+
+ private:
+ void Pop();
+
+ std::queue<Message*> queue_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessageQueue);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_MESSAGE_QUEUE_H_
diff --git a/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
new file mode 100644
index 0000000..1e85d4a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 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 "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/interfaces/bindings/tests/sample_import.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class ProviderImpl : public sample::Provider {
+ public:
+ explicit ProviderImpl(InterfaceRequest<sample::Provider> request)
+ : binding_(this, request.Pass()) {}
+
+ void EchoString(const String& a,
+ const Callback<void(String)>& callback) override {
+ Callback<void(String)> callback_copy;
+ // Make sure operator= is used.
+ callback_copy = callback;
+ callback_copy.Run(a);
+ }
+
+ void EchoStrings(const String& a,
+ const String& b,
+ const Callback<void(String, String)>& callback) override {
+ callback.Run(a, b);
+ }
+
+ void EchoMessagePipeHandle(
+ ScopedMessagePipeHandle a,
+ const Callback<void(ScopedMessagePipeHandle)>& callback) override {
+ callback.Run(a.Pass());
+ }
+
+ void EchoEnum(sample::Enum a,
+ const Callback<void(sample::Enum)>& callback) override {
+ callback.Run(a);
+ }
+
+ void EchoInt(int32_t a, const EchoIntCallback& callback) override {
+ callback.Run(a);
+ }
+
+ Binding<sample::Provider> binding_;
+};
+
+class StringRecorder {
+ public:
+ explicit StringRecorder(std::string* buf) : buf_(buf) {}
+ void Run(const String& a) const { *buf_ = a; }
+ void Run(const String& a, const String& b) const {
+ *buf_ = a.get() + b.get();
+ }
+
+ private:
+ std::string* buf_;
+};
+
+class EnumRecorder {
+ public:
+ explicit EnumRecorder(sample::Enum* value) : value_(value) {}
+ void Run(sample::Enum a) const { *value_ = a; }
+
+ private:
+ sample::Enum* value_;
+};
+
+class MessagePipeWriter {
+ public:
+ explicit MessagePipeWriter(const char* text) : text_(text) {}
+ void Run(ScopedMessagePipeHandle handle) const {
+ WriteTextMessage(handle.get(), text_);
+ }
+
+ private:
+ std::string text_;
+};
+
+class RequestResponseTest : public testing::Test {
+ public:
+ RequestResponseTest() : loop_(common::MessagePumpMojo::Create()) {}
+ ~RequestResponseTest() override { loop_.RunUntilIdle(); }
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ private:
+ base::MessageLoop loop_;
+};
+
+TEST_F(RequestResponseTest, EchoString) {
+ sample::ProviderPtr provider;
+ ProviderImpl provider_impl(GetProxy(&provider));
+
+ std::string buf;
+ provider->EchoString(String::From("hello"), StringRecorder(&buf));
+
+ PumpMessages();
+
+ EXPECT_EQ(std::string("hello"), buf);
+}
+
+TEST_F(RequestResponseTest, EchoStrings) {
+ sample::ProviderPtr provider;
+ ProviderImpl provider_impl(GetProxy(&provider));
+
+ std::string buf;
+ provider->EchoStrings(
+ String::From("hello"), String::From(" world"), StringRecorder(&buf));
+
+ PumpMessages();
+
+ EXPECT_EQ(std::string("hello world"), buf);
+}
+
+TEST_F(RequestResponseTest, EchoMessagePipeHandle) {
+ sample::ProviderPtr provider;
+ ProviderImpl provider_impl(GetProxy(&provider));
+
+ MessagePipe pipe2;
+ provider->EchoMessagePipeHandle(pipe2.handle1.Pass(),
+ MessagePipeWriter("hello"));
+
+ PumpMessages();
+
+ std::string value;
+ ReadTextMessage(pipe2.handle0.get(), &value);
+
+ EXPECT_EQ(std::string("hello"), value);
+}
+
+TEST_F(RequestResponseTest, EchoEnum) {
+ sample::ProviderPtr provider;
+ ProviderImpl provider_impl(GetProxy(&provider));
+
+ sample::Enum value;
+ provider->EchoEnum(sample::ENUM_VALUE, EnumRecorder(&value));
+
+ PumpMessages();
+
+ EXPECT_EQ(sample::ENUM_VALUE, value);
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/router_unittest.cc b/mojo/public/cpp/bindings/tests/router_unittest.cc
new file mode 100644
index 0000000..c9c9f01
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/router_unittest.cc
@@ -0,0 +1,379 @@
+// Copyright 2014 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 <stdlib.h>
+#include <string.h>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/bindings/tests/message_queue.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+void AllocRequestMessage(uint32_t name, const char* text, Message* message) {
+ size_t payload_size = strlen(text) + 1; // Plus null terminator.
+ internal::RequestMessageBuilder builder(name, payload_size);
+ memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
+
+ builder.message()->MoveTo(message);
+}
+
+void AllocResponseMessage(uint32_t name,
+ const char* text,
+ uint64_t request_id,
+ Message* message) {
+ size_t payload_size = strlen(text) + 1; // Plus null terminator.
+ internal::ResponseMessageBuilder builder(name, payload_size, request_id);
+ memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
+
+ builder.message()->MoveTo(message);
+}
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+ explicit MessageAccumulator(MessageQueue* queue) : queue_(queue) {}
+
+ bool Accept(Message* message) override {
+ queue_->Push(message);
+ return true;
+ }
+
+ private:
+ MessageQueue* queue_;
+};
+
+class ResponseGenerator : public MessageReceiverWithResponderStatus {
+ public:
+ ResponseGenerator() {}
+
+ bool Accept(Message* message) override { return false; }
+
+ bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder) override {
+ EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse));
+
+ bool result = SendResponse(
+ message->name(), message->request_id(),
+ reinterpret_cast<const char*>(message->payload()), responder);
+ EXPECT_TRUE(responder->IsValid());
+ delete responder;
+ return result;
+ }
+
+ bool SendResponse(uint32_t name,
+ uint64_t request_id,
+ const char* request_string,
+ MessageReceiver* responder) {
+ Message response;
+ std::string response_string(request_string);
+ response_string += " world!";
+ AllocResponseMessage(name, response_string.c_str(), request_id, &response);
+
+ return responder->Accept(&response);
+ }
+};
+
+class LazyResponseGenerator : public ResponseGenerator {
+ public:
+ LazyResponseGenerator() : responder_(nullptr), name_(0), request_id_(0) {}
+
+ ~LazyResponseGenerator() override { delete responder_; }
+
+ bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder) override {
+ name_ = message->name();
+ request_id_ = message->request_id();
+ request_string_ =
+ std::string(reinterpret_cast<const char*>(message->payload()));
+ responder_ = responder;
+ return true;
+ }
+
+ bool has_responder() const { return !!responder_; }
+
+ bool responder_is_valid() const { return responder_->IsValid(); }
+
+ // Send the response and delete the responder.
+ void CompleteWithResponse() { Complete(true); }
+
+ // Delete the responder without sending a response.
+ void CompleteWithoutResponse() { Complete(false); }
+
+ private:
+ // Completes the request handling by deleting responder_. Optionally
+ // also sends a response.
+ void Complete(bool send_response) {
+ if (send_response) {
+ SendResponse(name_, request_id_, request_string_.c_str(), responder_);
+ }
+ delete responder_;
+ responder_ = nullptr;
+ }
+
+ MessageReceiverWithStatus* responder_;
+ uint32_t name_;
+ uint64_t request_id_;
+ std::string request_string_;
+};
+
+class RouterTest : public testing::Test {
+ public:
+ RouterTest() : loop_(common::MessagePumpMojo::Create()) {}
+
+ void SetUp() override {
+ CreateMessagePipe(nullptr, &handle0_, &handle1_);
+ }
+
+ void TearDown() override {}
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ protected:
+ ScopedMessagePipeHandle handle0_;
+ ScopedMessagePipeHandle handle1_;
+
+ private:
+ base::MessageLoop loop_;
+};
+
+TEST_F(RouterTest, BasicRequestResponse) {
+ internal::Router router0(handle0_.Pass(), internal::FilterChain());
+ internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+ ResponseGenerator generator;
+ router1.set_incoming_receiver(&generator);
+
+ Message request;
+ AllocRequestMessage(1, "hello", &request);
+
+ MessageQueue message_queue;
+ router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+ PumpMessages();
+
+ EXPECT_FALSE(message_queue.IsEmpty());
+
+ Message response;
+ message_queue.Pop(&response);
+
+ EXPECT_EQ(std::string("hello world!"),
+ std::string(reinterpret_cast<const char*>(response.payload())));
+
+ // Send a second message on the pipe.
+ Message request2;
+ AllocRequestMessage(1, "hello again", &request2);
+
+ router0.AcceptWithResponder(&request2,
+ new MessageAccumulator(&message_queue));
+
+ PumpMessages();
+
+ EXPECT_FALSE(message_queue.IsEmpty());
+
+ message_queue.Pop(&response);
+
+ EXPECT_EQ(std::string("hello again world!"),
+ std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(RouterTest, BasicRequestResponse_Synchronous) {
+ internal::Router router0(handle0_.Pass(), internal::FilterChain());
+ internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+ ResponseGenerator generator;
+ router1.set_incoming_receiver(&generator);
+
+ Message request;
+ AllocRequestMessage(1, "hello", &request);
+
+ MessageQueue message_queue;
+ router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+ router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+ router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+ EXPECT_FALSE(message_queue.IsEmpty());
+
+ Message response;
+ message_queue.Pop(&response);
+
+ EXPECT_EQ(std::string("hello world!"),
+ std::string(reinterpret_cast<const char*>(response.payload())));
+
+ // Send a second message on the pipe.
+ Message request2;
+ AllocRequestMessage(1, "hello again", &request2);
+
+ router0.AcceptWithResponder(&request2,
+ new MessageAccumulator(&message_queue));
+
+ router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+ router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
+
+ EXPECT_FALSE(message_queue.IsEmpty());
+
+ message_queue.Pop(&response);
+
+ EXPECT_EQ(std::string("hello again world!"),
+ std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+TEST_F(RouterTest, RequestWithNoReceiver) {
+ internal::Router router0(handle0_.Pass(), internal::FilterChain());
+ internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+ // Without an incoming receiver set on router1, we expect router0 to observe
+ // an error as a result of sending a message.
+
+ Message request;
+ AllocRequestMessage(1, "hello", &request);
+
+ MessageQueue message_queue;
+ router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+ PumpMessages();
+
+ EXPECT_TRUE(router0.encountered_error());
+ EXPECT_TRUE(router1.encountered_error());
+ EXPECT_TRUE(message_queue.IsEmpty());
+}
+
+// Tests Router using the LazyResponseGenerator. The responses will not be
+// sent until after the requests have been accepted.
+TEST_F(RouterTest, LazyResponses) {
+ internal::Router router0(handle0_.Pass(), internal::FilterChain());
+ internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+ LazyResponseGenerator generator;
+ router1.set_incoming_receiver(&generator);
+
+ Message request;
+ AllocRequestMessage(1, "hello", &request);
+
+ MessageQueue message_queue;
+ router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+ PumpMessages();
+
+ // The request has been received but the response has not been sent yet.
+ EXPECT_TRUE(message_queue.IsEmpty());
+
+ // Send the response.
+ EXPECT_TRUE(generator.responder_is_valid());
+ generator.CompleteWithResponse();
+ PumpMessages();
+
+ // Check the response.
+ EXPECT_FALSE(message_queue.IsEmpty());
+ Message response;
+ message_queue.Pop(&response);
+ EXPECT_EQ(std::string("hello world!"),
+ std::string(reinterpret_cast<const char*>(response.payload())));
+
+ // Send a second message on the pipe.
+ Message request2;
+ AllocRequestMessage(1, "hello again", &request2);
+
+ router0.AcceptWithResponder(&request2,
+ new MessageAccumulator(&message_queue));
+ PumpMessages();
+
+ // The request has been received but the response has not been sent yet.
+ EXPECT_TRUE(message_queue.IsEmpty());
+
+ // Send the second response.
+ EXPECT_TRUE(generator.responder_is_valid());
+ generator.CompleteWithResponse();
+ PumpMessages();
+
+ // Check the second response.
+ EXPECT_FALSE(message_queue.IsEmpty());
+ message_queue.Pop(&response);
+ EXPECT_EQ(std::string("hello again world!"),
+ std::string(reinterpret_cast<const char*>(response.payload())));
+}
+
+// Tests that if the receiving application destroys the responder_ without
+// sending a response, then we trigger connection error at both sides. Moreover,
+// both sides still appear to have a valid message pipe handle bound.
+TEST_F(RouterTest, MissingResponses) {
+ internal::Router router0(handle0_.Pass(), internal::FilterChain());
+ bool error_handler_called0 = false;
+ router0.set_connection_error_handler(
+ [&error_handler_called0]() { error_handler_called0 = true; });
+
+ internal::Router router1(handle1_.Pass(), internal::FilterChain());
+ bool error_handler_called1 = false;
+ router1.set_connection_error_handler(
+ [&error_handler_called1]() { error_handler_called1 = true; });
+
+ LazyResponseGenerator generator;
+ router1.set_incoming_receiver(&generator);
+
+ Message request;
+ AllocRequestMessage(1, "hello", &request);
+
+ MessageQueue message_queue;
+ router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+ PumpMessages();
+
+ // The request has been received but no response has been sent.
+ EXPECT_TRUE(message_queue.IsEmpty());
+
+ // Destroy the responder MessagerReceiver but don't send any response.
+ generator.CompleteWithoutResponse();
+ PumpMessages();
+
+ // Check that no response was received.
+ EXPECT_TRUE(message_queue.IsEmpty());
+
+ // Connection error handler is called at both sides.
+ EXPECT_TRUE(error_handler_called0);
+ EXPECT_TRUE(error_handler_called1);
+
+ // The error flag is set at both sides.
+ EXPECT_TRUE(router0.encountered_error());
+ EXPECT_TRUE(router1.encountered_error());
+
+ // The message pipe handle is valid at both sides.
+ EXPECT_TRUE(router0.is_valid());
+ EXPECT_TRUE(router1.is_valid());
+}
+
+TEST_F(RouterTest, LateResponse) {
+ // Test that things won't blow up if we try to send a message to a
+ // MessageReceiver, which was given to us via AcceptWithResponder,
+ // after the router has gone away.
+
+ LazyResponseGenerator generator;
+ {
+ internal::Router router0(handle0_.Pass(), internal::FilterChain());
+ internal::Router router1(handle1_.Pass(), internal::FilterChain());
+
+ router1.set_incoming_receiver(&generator);
+
+ Message request;
+ AllocRequestMessage(1, "hello", &request);
+
+ MessageQueue message_queue;
+ router0.AcceptWithResponder(&request,
+ new MessageAccumulator(&message_queue));
+
+ PumpMessages();
+
+ EXPECT_TRUE(generator.has_responder());
+ }
+
+ EXPECT_FALSE(generator.responder_is_valid());
+ generator.CompleteWithResponse(); // This should end up doing nothing.
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
new file mode 100644
index 0000000..58878a7
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -0,0 +1,371 @@
+// Copyright 2014 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 <algorithm>
+#include <ostream>
+#include <string>
+
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+template <>
+struct TypeConverter<int32_t, sample::BarPtr> {
+ static int32_t Convert(const sample::BarPtr& bar) {
+ return static_cast<int32_t>(bar->alpha) << 16 |
+ static_cast<int32_t>(bar->beta) << 8 |
+ static_cast<int32_t>(bar->gamma);
+ }
+};
+
+} // namespace mojo
+
+namespace sample {
+namespace {
+
+// Set this variable to true to print the message in hex.
+bool g_dump_message_as_hex = false;
+
+// Set this variable to true to print the message in human readable form.
+bool g_dump_message_as_text = false;
+
+// Make a sample |Foo|.
+FooPtr MakeFoo() {
+ mojo::String name("foopy");
+
+ BarPtr bar(Bar::New());
+ bar->alpha = 20;
+ bar->beta = 40;
+ bar->gamma = 60;
+ bar->type = Bar::TYPE_VERTICAL;
+
+ mojo::Array<BarPtr> extra_bars(3);
+ for (size_t i = 0; i < extra_bars.size(); ++i) {
+ Bar::Type type = i % 2 == 0 ? Bar::TYPE_VERTICAL : Bar::TYPE_HORIZONTAL;
+ BarPtr bar(Bar::New());
+ uint8_t base = static_cast<uint8_t>(i * 100);
+ bar->alpha = base;
+ bar->beta = base + 20;
+ bar->gamma = base + 40;
+ bar->type = type;
+ extra_bars[i] = bar.Pass();
+ }
+
+ mojo::Array<uint8_t> data(10);
+ for (size_t i = 0; i < data.size(); ++i)
+ data[i] = static_cast<uint8_t>(data.size() - i);
+
+ mojo::Array<mojo::ScopedDataPipeConsumerHandle> input_streams(2);
+ mojo::Array<mojo::ScopedDataPipeProducerHandle> output_streams(2);
+ for (size_t i = 0; i < input_streams.size(); ++i) {
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = 1024;
+ mojo::ScopedDataPipeProducerHandle producer;
+ mojo::ScopedDataPipeConsumerHandle consumer;
+ mojo::CreateDataPipe(&options, &producer, &consumer);
+ input_streams[i] = consumer.Pass();
+ output_streams[i] = producer.Pass();
+ }
+
+ mojo::Array<mojo::Array<bool>> array_of_array_of_bools(2);
+ for (size_t i = 0; i < 2; ++i) {
+ mojo::Array<bool> array_of_bools(2);
+ for (size_t j = 0; j < 2; ++j)
+ array_of_bools[j] = j;
+ array_of_array_of_bools[i] = array_of_bools.Pass();
+ }
+
+ mojo::MessagePipe pipe;
+ FooPtr foo(Foo::New());
+ foo->name = name;
+ foo->x = 1;
+ foo->y = 2;
+ foo->a = false;
+ foo->b = true;
+ foo->c = false;
+ foo->bar = bar.Pass();
+ foo->extra_bars = extra_bars.Pass();
+ foo->data = data.Pass();
+ foo->source = pipe.handle1.Pass();
+ foo->input_streams = input_streams.Pass();
+ foo->output_streams = output_streams.Pass();
+ foo->array_of_array_of_bools = array_of_array_of_bools.Pass();
+
+ return foo.Pass();
+}
+
+// Check that the given |Foo| is identical to the one made by |MakeFoo()|.
+void CheckFoo(const Foo& foo) {
+ const std::string kName("foopy");
+ ASSERT_FALSE(foo.name.is_null());
+ EXPECT_EQ(kName.size(), foo.name.size());
+ for (size_t i = 0; i < std::min(kName.size(), foo.name.size()); i++) {
+ // Test both |operator[]| and |at|.
+ EXPECT_EQ(kName[i], foo.name.at(i)) << i;
+ EXPECT_EQ(kName[i], foo.name[i]) << i;
+ }
+ EXPECT_EQ(kName, foo.name.get());
+
+ EXPECT_EQ(1, foo.x);
+ EXPECT_EQ(2, foo.y);
+ EXPECT_FALSE(foo.a);
+ EXPECT_TRUE(foo.b);
+ EXPECT_FALSE(foo.c);
+
+ EXPECT_EQ(20, foo.bar->alpha);
+ EXPECT_EQ(40, foo.bar->beta);
+ EXPECT_EQ(60, foo.bar->gamma);
+ EXPECT_EQ(Bar::TYPE_VERTICAL, foo.bar->type);
+
+ EXPECT_EQ(3u, foo.extra_bars.size());
+ for (size_t i = 0; i < foo.extra_bars.size(); i++) {
+ uint8_t base = static_cast<uint8_t>(i * 100);
+ Bar::Type type = i % 2 == 0 ? Bar::TYPE_VERTICAL : Bar::TYPE_HORIZONTAL;
+ EXPECT_EQ(base, foo.extra_bars[i]->alpha) << i;
+ EXPECT_EQ(base + 20, foo.extra_bars[i]->beta) << i;
+ EXPECT_EQ(base + 40, foo.extra_bars[i]->gamma) << i;
+ EXPECT_EQ(type, foo.extra_bars[i]->type) << i;
+ }
+
+ EXPECT_EQ(10u, foo.data.size());
+ for (size_t i = 0; i < foo.data.size(); ++i) {
+ EXPECT_EQ(static_cast<uint8_t>(foo.data.size() - i), foo.data[i]) << i;
+ }
+
+ EXPECT_FALSE(foo.input_streams.is_null());
+ EXPECT_EQ(2u, foo.input_streams.size());
+
+ EXPECT_FALSE(foo.output_streams.is_null());
+ EXPECT_EQ(2u, foo.output_streams.size());
+
+ EXPECT_EQ(2u, foo.array_of_array_of_bools.size());
+ for (size_t i = 0; i < foo.array_of_array_of_bools.size(); ++i) {
+ EXPECT_EQ(2u, foo.array_of_array_of_bools[i].size());
+ for (size_t j = 0; j < foo.array_of_array_of_bools[i].size(); ++j) {
+ EXPECT_EQ(bool(j), foo.array_of_array_of_bools[i][j]);
+ }
+ }
+}
+
+void PrintSpacer(int depth) {
+ for (int i = 0; i < depth; ++i)
+ std::cout << " ";
+}
+
+void Print(int depth, const char* name, bool value) {
+ PrintSpacer(depth);
+ std::cout << name << ": " << (value ? "true" : "false") << std::endl;
+}
+
+void Print(int depth, const char* name, int32_t value) {
+ PrintSpacer(depth);
+ std::cout << name << ": " << value << std::endl;
+}
+
+void Print(int depth, const char* name, uint8_t value) {
+ PrintSpacer(depth);
+ std::cout << name << ": " << uint32_t(value) << std::endl;
+}
+
+template <typename H>
+void Print(int depth,
+ const char* name,
+ const mojo::ScopedHandleBase<H>& value) {
+ PrintSpacer(depth);
+ std::cout << name << ": 0x" << std::hex << value.get().value() << std::endl;
+}
+
+void Print(int depth, const char* name, const mojo::String& str) {
+ PrintSpacer(depth);
+ std::cout << name << ": \"" << str.get() << "\"" << std::endl;
+}
+
+void Print(int depth, const char* name, const BarPtr& bar) {
+ PrintSpacer(depth);
+ std::cout << name << ":" << std::endl;
+ if (!bar.is_null()) {
+ ++depth;
+ Print(depth, "alpha", bar->alpha);
+ Print(depth, "beta", bar->beta);
+ Print(depth, "gamma", bar->gamma);
+ Print(depth, "packed", bar.To<int32_t>());
+ --depth;
+ }
+}
+
+template <typename T>
+void Print(int depth, const char* name, const mojo::Array<T>& array) {
+ PrintSpacer(depth);
+ std::cout << name << ":" << std::endl;
+ if (!array.is_null()) {
+ ++depth;
+ for (size_t i = 0; i < array.size(); ++i) {
+ std::stringstream buf;
+ buf << i;
+ Print(depth, buf.str().data(), array.at(i));
+ }
+ --depth;
+ }
+}
+
+void Print(int depth, const char* name, const FooPtr& foo) {
+ PrintSpacer(depth);
+ std::cout << name << ":" << std::endl;
+ if (!foo.is_null()) {
+ ++depth;
+ Print(depth, "name", foo->name);
+ Print(depth, "x", foo->x);
+ Print(depth, "y", foo->y);
+ Print(depth, "a", foo->a);
+ Print(depth, "b", foo->b);
+ Print(depth, "c", foo->c);
+ Print(depth, "bar", foo->bar);
+ Print(depth, "extra_bars", foo->extra_bars);
+ Print(depth, "data", foo->data);
+ Print(depth, "source", foo->source);
+ Print(depth, "input_streams", foo->input_streams);
+ Print(depth, "output_streams", foo->output_streams);
+ Print(depth, "array_of_array_of_bools", foo->array_of_array_of_bools);
+ --depth;
+ }
+}
+
+void DumpHex(const uint8_t* bytes, uint32_t num_bytes) {
+ for (uint32_t i = 0; i < num_bytes; ++i) {
+ std::cout << std::setw(2) << std::setfill('0') << std::hex
+ << uint32_t(bytes[i]);
+
+ if (i % 16 == 15) {
+ std::cout << std::endl;
+ continue;
+ }
+
+ if (i % 2 == 1)
+ std::cout << " ";
+ if (i % 8 == 7)
+ std::cout << " ";
+ }
+}
+
+class ServiceImpl : public Service {
+ public:
+ void Frobinate(FooPtr foo,
+ BazOptions baz,
+ PortPtr port,
+ const Service::FrobinateCallback& callback) override {
+ // Users code goes here to handle the incoming Frobinate message.
+
+ // We mainly check that we're given the expected arguments.
+ EXPECT_FALSE(foo.is_null());
+ if (!foo.is_null())
+ CheckFoo(*foo);
+ EXPECT_EQ(BAZ_OPTIONS_EXTRA, baz);
+
+ if (g_dump_message_as_text) {
+ // Also dump the Foo structure and all of its members.
+ std::cout << "Frobinate:" << std::endl;
+ int depth = 1;
+ Print(depth, "foo", foo);
+ Print(depth, "baz", baz);
+ Print(depth, "port", port.get());
+ }
+ callback.Run(5);
+ }
+
+ void GetPort(mojo::InterfaceRequest<Port> port_request) override {}
+};
+
+class ServiceProxyImpl : public ServiceProxy {
+ public:
+ explicit ServiceProxyImpl(mojo::MessageReceiverWithResponder* receiver)
+ : ServiceProxy(receiver) {}
+};
+
+class SimpleMessageReceiver : public mojo::MessageReceiverWithResponder {
+ public:
+ bool Accept(mojo::Message* message) override {
+ // Imagine some IPC happened here.
+
+ if (g_dump_message_as_hex) {
+ DumpHex(reinterpret_cast<const uint8_t*>(message->data()),
+ message->data_num_bytes());
+ }
+
+ // In the receiving process, an implementation of ServiceStub is known to
+ // the system. It receives the incoming message.
+ ServiceImpl impl;
+
+ ServiceStub stub;
+ stub.set_sink(&impl);
+ return stub.Accept(message);
+ }
+
+ bool AcceptWithResponder(mojo::Message* message,
+ mojo::MessageReceiver* responder) override {
+ return false;
+ }
+};
+
+using BindingsSampleTest = testing::Test;
+
+TEST_F(BindingsSampleTest, Basic) {
+ SimpleMessageReceiver receiver;
+
+ // User has a proxy to a Service somehow.
+ Service* service = new ServiceProxyImpl(&receiver);
+
+ // User constructs a message to send.
+
+ // Notice that it doesn't matter in what order the structs / arrays are
+ // allocated. Here, the various members of Foo are allocated before Foo is
+ // allocated.
+
+ FooPtr foo = MakeFoo();
+ CheckFoo(*foo);
+
+ PortPtr port;
+ service->Frobinate(foo.Pass(), Service::BAZ_OPTIONS_EXTRA, port.Pass(),
+ Service::FrobinateCallback());
+
+ delete service;
+}
+
+TEST_F(BindingsSampleTest, DefaultValues) {
+ DefaultsTestPtr defaults(DefaultsTest::New());
+ EXPECT_EQ(-12, defaults->a0);
+ EXPECT_EQ(kTwelve, defaults->a1);
+ EXPECT_EQ(1234, defaults->a2);
+ EXPECT_EQ(34567U, defaults->a3);
+ EXPECT_EQ(123456, defaults->a4);
+ EXPECT_EQ(3456789012U, defaults->a5);
+ EXPECT_EQ(-111111111111LL, defaults->a6);
+ EXPECT_EQ(9999999999999999999ULL, defaults->a7);
+ EXPECT_EQ(0x12345, defaults->a8);
+ EXPECT_EQ(-0x12345, defaults->a9);
+ EXPECT_EQ(1234, defaults->a10);
+ EXPECT_TRUE(defaults->a11);
+ EXPECT_FALSE(defaults->a12);
+ EXPECT_FLOAT_EQ(123.25f, defaults->a13);
+ EXPECT_DOUBLE_EQ(1234567890.123, defaults->a14);
+ EXPECT_DOUBLE_EQ(1E10, defaults->a15);
+ EXPECT_DOUBLE_EQ(-1.2E+20, defaults->a16);
+ EXPECT_DOUBLE_EQ(1.23E-20, defaults->a17);
+ EXPECT_TRUE(defaults->a18.is_null());
+ EXPECT_TRUE(defaults->a19.is_null());
+ EXPECT_EQ(Bar::TYPE_BOTH, defaults->a20);
+ EXPECT_TRUE(defaults->a21.is_null());
+ ASSERT_FALSE(defaults->a22.is_null());
+ EXPECT_EQ(imported::SHAPE_RECTANGLE, defaults->a22->shape);
+ EXPECT_EQ(imported::COLOR_BLACK, defaults->a22->color);
+ EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, defaults->a23);
+ EXPECT_EQ(0x123456789, defaults->a24);
+ EXPECT_EQ(-0x123456789, defaults->a25);
+}
+
+} // namespace
+} // namespace sample
diff --git a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
new file mode 100644
index 0000000..15da051
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2014 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.
+
+// Serialization warnings are only recorded in debug build.
+#ifndef NDEBUG
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using mojo::internal::ArrayValidateParams;
+
+// Creates an array of arrays of handles (2 X 3) for testing.
+Array<Array<ScopedHandle>> CreateTestNestedHandleArray() {
+ Array<Array<ScopedHandle>> array(2);
+ for (size_t i = 0; i < array.size(); ++i) {
+ Array<ScopedHandle> nested_array(3);
+ for (size_t j = 0; j < nested_array.size(); ++j) {
+ MessagePipe pipe;
+ nested_array[j] = ScopedHandle::From(pipe.handle1.Pass());
+ }
+ array[i] = nested_array.Pass();
+ }
+
+ return array.Pass();
+}
+
+class SerializationWarningTest : public testing::Test {
+ public:
+ ~SerializationWarningTest() override {}
+
+ protected:
+ template <typename T>
+ void TestWarning(StructPtr<T> obj,
+ mojo::internal::ValidationError expected_warning) {
+ TestStructWarningImpl<T>(obj.Pass(), expected_warning);
+ }
+
+ template <typename T>
+ void TestWarning(InlinedStructPtr<T> obj,
+ mojo::internal::ValidationError expected_warning) {
+ TestStructWarningImpl<T>(obj.Pass(), expected_warning);
+ }
+
+ template <typename T, typename TPtr>
+ void TestStructWarningImpl(TPtr obj,
+ mojo::internal::ValidationError expected_warning) {
+ warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+ mojo::internal::FixedBufferForTesting buf(GetSerializedSize_(obj));
+ typename T::Data_* data;
+ Serialize_(obj.Pass(), &buf, &data);
+
+ EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+ }
+
+ template <typename T>
+ void TestArrayWarning(T obj,
+ mojo::internal::ValidationError expected_warning,
+ const ArrayValidateParams* validate_params) {
+ warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+ mojo::internal::FixedBufferForTesting buf(GetSerializedSize_(obj));
+ typename T::Data_* data;
+ SerializeArray_(obj.Pass(), &buf, &data, validate_params);
+
+ EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+ }
+
+ mojo::internal::SerializationWarningObserverForTesting warning_observer_;
+};
+
+TEST_F(SerializationWarningTest, HandleInStruct) {
+ Struct2Ptr test_struct(Struct2::New());
+ EXPECT_FALSE(test_struct->hdl.is_valid());
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE);
+
+ test_struct = Struct2::New();
+ MessagePipe pipe;
+ test_struct->hdl = ScopedHandle::From(pipe.handle1.Pass());
+
+ TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, StructInStruct) {
+ Struct3Ptr test_struct(Struct3::New());
+ EXPECT_TRUE(!test_struct->struct_1);
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_struct = Struct3::New();
+ test_struct->struct_1 = Struct1::New();
+
+ TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStructsInStruct) {
+ Struct4Ptr test_struct(Struct4::New());
+ EXPECT_TRUE(!test_struct->data);
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_struct = Struct4::New();
+ test_struct->data.resize(1);
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_struct = Struct4::New();
+ test_struct->data.resize(0);
+
+ TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+
+ test_struct = Struct4::New();
+ test_struct->data.resize(1);
+ test_struct->data[0] = Struct1::New();
+
+ TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, FixedArrayOfStructsInStruct) {
+ Struct5Ptr test_struct(Struct5::New());
+ EXPECT_TRUE(!test_struct->pair);
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_struct = Struct5::New();
+ test_struct->pair.resize(1);
+ test_struct->pair[0] = Struct1::New();
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+
+ test_struct = Struct5::New();
+ test_struct->pair.resize(2);
+ test_struct->pair[0] = Struct1::New();
+ test_struct->pair[1] = Struct1::New();
+
+ TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, StringInStruct) {
+ Struct6Ptr test_struct(Struct6::New());
+ EXPECT_TRUE(!test_struct->str);
+
+ TestWarning(test_struct.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_struct = Struct6::New();
+ test_struct->str = "hello world";
+
+ TestWarning(test_struct.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfArraysOfHandles) {
+ Array<Array<ScopedHandle>> test_array = CreateTestNestedHandleArray();
+ test_array[0] = Array<ScopedHandle>();
+ test_array[1][0] = ScopedHandle();
+
+ ArrayValidateParams validate_params_0(
+ 0, true, new ArrayValidateParams(0, true, nullptr));
+ TestArrayWarning(test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE,
+ &validate_params_0);
+
+ test_array = CreateTestNestedHandleArray();
+ test_array[0] = Array<ScopedHandle>();
+ ArrayValidateParams validate_params_1(
+ 0, false, new ArrayValidateParams(0, true, nullptr));
+ TestArrayWarning(test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ &validate_params_1);
+
+ test_array = CreateTestNestedHandleArray();
+ test_array[1][0] = ScopedHandle();
+ ArrayValidateParams validate_params_2(
+ 0, true, new ArrayValidateParams(0, false, nullptr));
+ TestArrayWarning(test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ &validate_params_2);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStrings) {
+ Array<String> test_array(3);
+ for (size_t i = 0; i < test_array.size(); ++i)
+ test_array[i] = "hello";
+
+ ArrayValidateParams validate_params_0(
+ 0, true, new ArrayValidateParams(0, false, nullptr));
+ TestArrayWarning(test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE,
+ &validate_params_0);
+
+ test_array = Array<String>(3);
+ ArrayValidateParams validate_params_1(
+ 0, false, new ArrayValidateParams(0, false, nullptr));
+ TestArrayWarning(test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ &validate_params_1);
+
+ test_array = Array<String>(2);
+ ArrayValidateParams validate_params_2(
+ 3, true, new ArrayValidateParams(0, false, nullptr));
+ TestArrayWarning(test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ &validate_params_2);
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
+
+#endif
diff --git a/mojo/public/cpp/bindings/tests/string_unittest.cc b/mojo/public/cpp/bindings/tests/string_unittest.cc
new file mode 100644
index 0000000..f6bc424
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/string_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright 2014 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/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+TEST(StringTest, DefaultIsNull) {
+ String s;
+ EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, ConstructedWithNULL) {
+ String s(nullptr);
+ EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, ConstructedWithNullCharPointer) {
+ const char* null = nullptr;
+ String s(null);
+ EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, AssignedNULL) {
+ String s("");
+ EXPECT_FALSE(s.is_null());
+ s = nullptr;
+ EXPECT_TRUE(s.is_null());
+}
+
+TEST(StringTest, Empty) {
+ String s("");
+ EXPECT_FALSE(s.is_null());
+ EXPECT_TRUE(s.get().empty());
+}
+
+TEST(StringTest, Basic) {
+ String s("hello world");
+ EXPECT_EQ(std::string("hello world"), s.get());
+}
+
+TEST(StringTest, Assignment) {
+ String s("hello world");
+ String t = s; // Makes a copy.
+ EXPECT_FALSE(t.is_null());
+ EXPECT_EQ(std::string("hello world"), t.get());
+ EXPECT_FALSE(s.is_null());
+}
+
+TEST(StringTest, Equality) {
+ String s("hello world");
+ String t("hello world");
+ EXPECT_EQ(s, t);
+ EXPECT_TRUE(s == s);
+ EXPECT_FALSE(s != s);
+ EXPECT_TRUE(s == t);
+ EXPECT_FALSE(s != t);
+ EXPECT_TRUE("hello world" == s);
+ EXPECT_TRUE(s == "hello world");
+ EXPECT_TRUE("not" != s);
+ EXPECT_FALSE("not" == s);
+ EXPECT_TRUE(s != "not");
+ EXPECT_FALSE(s == "not");
+
+ // Test null strings.
+ String n1;
+ String n2;
+ EXPECT_TRUE(n1 == n1);
+ EXPECT_FALSE(n1 != n2);
+ EXPECT_TRUE(n1 == n2);
+ EXPECT_FALSE(n1 != n2);
+ EXPECT_TRUE(n1 != s);
+ EXPECT_FALSE(n1 == s);
+ EXPECT_TRUE(s != n1);
+ EXPECT_FALSE(s == n1);
+}
+
+TEST(StringTest, LessThanNullness) {
+ String null;
+ String null2;
+ EXPECT_FALSE(null < null2);
+ EXPECT_FALSE(null2 < null);
+
+ String real("real");
+ EXPECT_TRUE(null < real);
+ EXPECT_FALSE(real < null);
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc
new file mode 100644
index 0000000..24173e5
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -0,0 +1,415 @@
+// Copyright 2014 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 <string.h>
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+RectPtr MakeRect(int32_t factor = 1) {
+ RectPtr rect(Rect::New());
+ rect->x = 1 * factor;
+ rect->y = 2 * factor;
+ rect->width = 10 * factor;
+ rect->height = 20 * factor;
+ return rect.Pass();
+}
+
+void CheckRect(const Rect& rect, int32_t factor = 1) {
+ EXPECT_EQ(1 * factor, rect.x);
+ EXPECT_EQ(2 * factor, rect.y);
+ EXPECT_EQ(10 * factor, rect.width);
+ EXPECT_EQ(20 * factor, rect.height);
+}
+
+MultiVersionStructPtr MakeMultiVersionStruct() {
+ MultiVersionStructPtr output(MultiVersionStruct::New());
+ output->f_int32 = 123;
+ output->f_rect = MakeRect(5);
+ output->f_string = "hello";
+ output->f_array = Array<int8_t>(3);
+ output->f_array[0] = 10;
+ output->f_array[1] = 9;
+ output->f_array[2] = 8;
+ MessagePipe pipe;
+ output->f_message_pipe = pipe.handle0.Pass();
+ output->f_int16 = 42;
+
+ return output.Pass();
+}
+
+template <typename U, typename T>
+U SerializeAndDeserialize(T input) {
+ typedef typename mojo::internal::WrapperTraits<T>::DataType InputDataType;
+ typedef typename mojo::internal::WrapperTraits<U>::DataType OutputDataType;
+
+ size_t size = GetSerializedSize_(input);
+ mojo::internal::FixedBufferForTesting buf(size + 32);
+ InputDataType data;
+ Serialize_(input.Pass(), &buf, &data);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+
+ // Set the subsequent area to a special value, so that we can find out if we
+ // mistakenly access the area.
+ void* subsequent_area = buf.Allocate(32);
+ memset(subsequent_area, 0xAA, 32);
+
+ OutputDataType output_data = reinterpret_cast<OutputDataType>(data);
+ output_data->DecodePointersAndHandles(&handles);
+
+ U output;
+ Deserialize_(output_data, &output);
+ return output.Pass();
+}
+
+using StructTest = testing::Test;
+
+} // namespace
+
+TEST_F(StructTest, Rect) {
+ RectPtr rect;
+ EXPECT_TRUE(rect.is_null());
+ EXPECT_TRUE(!rect);
+ EXPECT_FALSE(rect);
+
+ rect = nullptr;
+ EXPECT_TRUE(rect.is_null());
+ EXPECT_TRUE(!rect);
+ EXPECT_FALSE(rect);
+
+ rect = MakeRect();
+ EXPECT_FALSE(rect.is_null());
+ EXPECT_FALSE(!rect);
+ EXPECT_TRUE(rect);
+
+ RectPtr null_rect = nullptr;
+ EXPECT_TRUE(null_rect.is_null());
+ EXPECT_TRUE(!null_rect);
+ EXPECT_FALSE(null_rect);
+
+ CheckRect(*rect);
+}
+
+TEST_F(StructTest, Clone) {
+ NamedRegionPtr region;
+
+ NamedRegionPtr clone_region = region.Clone();
+ EXPECT_TRUE(clone_region.is_null());
+
+ region = NamedRegion::New();
+ clone_region = region.Clone();
+ EXPECT_TRUE(clone_region->name.is_null());
+ EXPECT_TRUE(clone_region->rects.is_null());
+
+ region->name = "hello world";
+ clone_region = region.Clone();
+ EXPECT_EQ(region->name, clone_region->name);
+
+ region->rects = Array<RectPtr>(2);
+ region->rects[1] = MakeRect();
+ clone_region = region.Clone();
+ EXPECT_EQ(2u, clone_region->rects.size());
+ EXPECT_TRUE(clone_region->rects[0].is_null());
+ CheckRect(*clone_region->rects[1]);
+
+ // NoDefaultFieldValues contains handles, so Clone() is not available, but
+ // NoDefaultFieldValuesPtr should still compile.
+ NoDefaultFieldValuesPtr no_default_field_values(NoDefaultFieldValues::New());
+ EXPECT_FALSE(no_default_field_values->f13.is_valid());
+}
+
+// Serialization test of a struct with no pointer or handle members.
+TEST_F(StructTest, Serialization_Basic) {
+ RectPtr rect(MakeRect());
+
+ size_t size = GetSerializedSize_(rect);
+ EXPECT_EQ(8U + 16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::Rect_Data* data;
+ Serialize_(rect.Pass(), &buf, &data);
+
+ RectPtr rect2;
+ Deserialize_(data, &rect2);
+
+ CheckRect(*rect2);
+}
+
+// Construction of a struct with struct pointers from null.
+TEST_F(StructTest, Construction_StructPointers) {
+ RectPairPtr pair;
+ EXPECT_TRUE(pair.is_null());
+
+ pair = RectPair::New();
+ EXPECT_FALSE(pair.is_null());
+ EXPECT_TRUE(pair->first.is_null());
+ EXPECT_TRUE(pair->first.is_null());
+
+ pair = nullptr;
+ EXPECT_TRUE(pair.is_null());
+}
+
+// Serialization test of a struct with struct pointers.
+TEST_F(StructTest, Serialization_StructPointers) {
+ RectPairPtr pair(RectPair::New());
+ pair->first = MakeRect();
+ pair->second = MakeRect();
+
+ size_t size = GetSerializedSize_(pair);
+ EXPECT_EQ(8U + 16U + 2 * (8U + 16U), size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::RectPair_Data* data;
+ Serialize_(pair.Pass(), &buf, &data);
+
+ RectPairPtr pair2;
+ Deserialize_(data, &pair2);
+
+ CheckRect(*pair2->first);
+ CheckRect(*pair2->second);
+}
+
+// Serialization test of a struct with an array member.
+TEST_F(StructTest, Serialization_ArrayPointers) {
+ NamedRegionPtr region(NamedRegion::New());
+ region->name = "region";
+ region->rects = Array<RectPtr>::New(4);
+ for (size_t i = 0; i < region->rects.size(); ++i)
+ region->rects[i] = MakeRect(static_cast<int32_t>(i) + 1);
+
+ size_t size = GetSerializedSize_(region);
+ EXPECT_EQ(8U + // header
+ 8U + // name pointer
+ 8U + // rects pointer
+ 8U + // name header
+ 8U + // name payload (rounded up)
+ 8U + // rects header
+ 4 * 8U + // rects payload (four pointers)
+ 4 * (8U + // rect header
+ 16U), // rect payload (four ints)
+ size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::NamedRegion_Data* data;
+ Serialize_(region.Pass(), &buf, &data);
+
+ NamedRegionPtr region2;
+ Deserialize_(data, &region2);
+
+ EXPECT_EQ(String("region"), region2->name);
+
+ EXPECT_EQ(4U, region2->rects.size());
+ for (size_t i = 0; i < region2->rects.size(); ++i)
+ CheckRect(*region2->rects[i], static_cast<int32_t>(i) + 1);
+}
+
+// Serialization test of a struct with null array pointers.
+TEST_F(StructTest, Serialization_NullArrayPointers) {
+ NamedRegionPtr region(NamedRegion::New());
+ EXPECT_TRUE(region->name.is_null());
+ EXPECT_TRUE(region->rects.is_null());
+
+ size_t size = GetSerializedSize_(region);
+ EXPECT_EQ(8U + // header
+ 8U + // name pointer
+ 8U, // rects pointer
+ size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::NamedRegion_Data* data;
+ Serialize_(region.Pass(), &buf, &data);
+
+ NamedRegionPtr region2;
+ Deserialize_(data, &region2);
+
+ EXPECT_TRUE(region2->name.is_null());
+ EXPECT_TRUE(region2->rects.is_null());
+}
+
+// Tests deserializing structs as a newer version.
+TEST_F(StructTest, Versioning_OldToNew) {
+ {
+ MultiVersionStructV0Ptr input(MultiVersionStructV0::New());
+ input->f_int32 = 123;
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+ expected_output->f_int32 = 123;
+
+ MultiVersionStructPtr output =
+ SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructV1Ptr input(MultiVersionStructV1::New());
+ input->f_int32 = 123;
+ input->f_rect = MakeRect(5);
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+
+ MultiVersionStructPtr output =
+ SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructV3Ptr input(MultiVersionStructV3::New());
+ input->f_int32 = 123;
+ input->f_rect = MakeRect(5);
+ input->f_string = "hello";
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+ expected_output->f_string = "hello";
+
+ MultiVersionStructPtr output =
+ SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructV5Ptr input(MultiVersionStructV5::New());
+ input->f_int32 = 123;
+ input->f_rect = MakeRect(5);
+ input->f_string = "hello";
+ input->f_array = Array<int8_t>(3);
+ input->f_array[0] = 10;
+ input->f_array[1] = 9;
+ input->f_array[2] = 8;
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+ expected_output->f_string = "hello";
+ expected_output->f_array = Array<int8_t>(3);
+ expected_output->f_array[0] = 10;
+ expected_output->f_array[1] = 9;
+ expected_output->f_array[2] = 8;
+
+ MultiVersionStructPtr output =
+ SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructV7Ptr input(MultiVersionStructV7::New());
+ input->f_int32 = 123;
+ input->f_rect = MakeRect(5);
+ input->f_string = "hello";
+ input->f_array = Array<int8_t>(3);
+ input->f_array[0] = 10;
+ input->f_array[1] = 9;
+ input->f_array[2] = 8;
+ MessagePipe pipe;
+ input->f_message_pipe = pipe.handle0.Pass();
+
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+ expected_output->f_string = "hello";
+ expected_output->f_array = Array<int8_t>(3);
+ expected_output->f_array[0] = 10;
+ expected_output->f_array[1] = 9;
+ expected_output->f_array[2] = 8;
+ // Save the raw handle value separately so that we can compare later.
+ MojoHandle expected_handle = input->f_message_pipe.get().value();
+
+ MultiVersionStructPtr output =
+ SerializeAndDeserialize<MultiVersionStructPtr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_EQ(expected_handle, output->f_message_pipe.get().value());
+ output->f_message_pipe.reset();
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+}
+
+// Tests deserializing structs as an older version.
+TEST_F(StructTest, Versioning_NewToOld) {
+ {
+ MultiVersionStructPtr input = MakeMultiVersionStruct();
+ MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+ expected_output->f_string = "hello";
+ expected_output->f_array = Array<int8_t>(3);
+ expected_output->f_array[0] = 10;
+ expected_output->f_array[1] = 9;
+ expected_output->f_array[2] = 8;
+ // Save the raw handle value separately so that we can compare later.
+ MojoHandle expected_handle = input->f_message_pipe.get().value();
+
+ MultiVersionStructV7Ptr output =
+ SerializeAndDeserialize<MultiVersionStructV7Ptr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_EQ(expected_handle, output->f_message_pipe.get().value());
+ output->f_message_pipe.reset();
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructPtr input = MakeMultiVersionStruct();
+ MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+ expected_output->f_string = "hello";
+ expected_output->f_array = Array<int8_t>(3);
+ expected_output->f_array[0] = 10;
+ expected_output->f_array[1] = 9;
+ expected_output->f_array[2] = 8;
+
+ MultiVersionStructV5Ptr output =
+ SerializeAndDeserialize<MultiVersionStructV5Ptr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructPtr input = MakeMultiVersionStruct();
+ MultiVersionStructV3Ptr expected_output(MultiVersionStructV3::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+ expected_output->f_string = "hello";
+
+ MultiVersionStructV3Ptr output =
+ SerializeAndDeserialize<MultiVersionStructV3Ptr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructPtr input = MakeMultiVersionStruct();
+ MultiVersionStructV1Ptr expected_output(MultiVersionStructV1::New());
+ expected_output->f_int32 = 123;
+ expected_output->f_rect = MakeRect(5);
+
+ MultiVersionStructV1Ptr output =
+ SerializeAndDeserialize<MultiVersionStructV1Ptr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+
+ {
+ MultiVersionStructPtr input = MakeMultiVersionStruct();
+ MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New());
+ expected_output->f_int32 = 123;
+
+ MultiVersionStructV0Ptr output =
+ SerializeAndDeserialize<MultiVersionStructV0Ptr>(input.Pass());
+ EXPECT_TRUE(output);
+ EXPECT_TRUE(output->Equals(*expected_output));
+ }
+}
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
new file mode 100644
index 0000000..704110a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright 2013 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/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+struct RedmondRect {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+};
+
+struct RedmondNamedRegion {
+ std::string name;
+ std::vector<RedmondRect> rects;
+};
+
+bool AreEqualRectArrays(const Array<test::RectPtr>& rects1,
+ const Array<test::RectPtr>& rects2) {
+ if (rects1.size() != rects2.size())
+ return false;
+
+ for (size_t i = 0; i < rects1.size(); ++i) {
+ if (rects1[i]->x != rects2[i]->x || rects1[i]->y != rects2[i]->y ||
+ rects1[i]->width != rects2[i]->width ||
+ rects1[i]->height != rects2[i]->height) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+template <>
+struct TypeConverter<test::RectPtr, RedmondRect> {
+ static test::RectPtr Convert(const RedmondRect& input) {
+ test::RectPtr rect(test::Rect::New());
+ rect->x = input.left;
+ rect->y = input.top;
+ rect->width = input.right - input.left;
+ rect->height = input.bottom - input.top;
+ return rect.Pass();
+ }
+};
+
+template <>
+struct TypeConverter<RedmondRect, test::RectPtr> {
+ static RedmondRect Convert(const test::RectPtr& input) {
+ RedmondRect rect;
+ rect.left = input->x;
+ rect.top = input->y;
+ rect.right = input->x + input->width;
+ rect.bottom = input->y + input->height;
+ return rect;
+ }
+};
+
+template <>
+struct TypeConverter<test::NamedRegionPtr, RedmondNamedRegion> {
+ static test::NamedRegionPtr Convert(const RedmondNamedRegion& input) {
+ test::NamedRegionPtr region(test::NamedRegion::New());
+ region->name = input.name;
+ region->rects = Array<test::RectPtr>::From(input.rects);
+ return region.Pass();
+ }
+};
+
+template <>
+struct TypeConverter<RedmondNamedRegion, test::NamedRegionPtr> {
+ static RedmondNamedRegion Convert(const test::NamedRegionPtr& input) {
+ RedmondNamedRegion region;
+ region.name = input->name;
+ region.rects = input->rects.To<std::vector<RedmondRect>>();
+ return region;
+ }
+};
+
+namespace test {
+namespace {
+
+TEST(TypeConversionTest, String) {
+ const char kText[6] = "hello";
+
+ String a = std::string(kText);
+ String b(kText);
+ String c(static_cast<const char*>(kText));
+
+ EXPECT_EQ(std::string(kText), a.To<std::string>());
+ EXPECT_EQ(std::string(kText), b.To<std::string>());
+ EXPECT_EQ(std::string(kText), c.To<std::string>());
+}
+
+TEST(TypeConversionTest, String_Null) {
+ String a;
+ EXPECT_TRUE(a.is_null());
+ EXPECT_EQ(std::string(), a.To<std::string>());
+
+ String b = String::From(static_cast<const char*>(nullptr));
+ EXPECT_TRUE(b.is_null());
+}
+
+TEST(TypeConversionTest, String_Empty) {
+ String a = "";
+ EXPECT_EQ(std::string(), a.To<std::string>());
+
+ String b = std::string();
+ EXPECT_FALSE(b.is_null());
+ EXPECT_EQ(std::string(), b.To<std::string>());
+}
+
+TEST(TypeConversionTest, StringWithEmbeddedNull) {
+ const std::string kText("hel\0lo", 6);
+
+ String a(kText);
+ EXPECT_EQ(kText, a.To<std::string>());
+
+ // Expect truncation:
+ String b(kText.c_str());
+ EXPECT_EQ(std::string("hel"), b.To<std::string>());
+}
+
+TEST(TypeConversionTest, CustomTypeConverter) {
+ RectPtr rect(Rect::New());
+ rect->x = 10;
+ rect->y = 20;
+ rect->width = 50;
+ rect->height = 45;
+
+ RedmondRect rr = rect.To<RedmondRect>();
+ EXPECT_EQ(10, rr.left);
+ EXPECT_EQ(20, rr.top);
+ EXPECT_EQ(60, rr.right);
+ EXPECT_EQ(65, rr.bottom);
+
+ RectPtr rect2(Rect::From(rr));
+ EXPECT_EQ(rect->x, rect2->x);
+ EXPECT_EQ(rect->y, rect2->y);
+ EXPECT_EQ(rect->width, rect2->width);
+ EXPECT_EQ(rect->height, rect2->height);
+}
+
+TEST(TypeConversionTest, CustomTypeConverter_Array_Null) {
+ Array<RectPtr> rects;
+
+ std::vector<RedmondRect> redmond_rects = rects.To<std::vector<RedmondRect>>();
+
+ EXPECT_TRUE(redmond_rects.empty());
+}
+
+TEST(TypeConversionTest, CustomTypeConverter_Array) {
+ const RedmondRect kBase = {10, 20, 30, 40};
+
+ Array<RectPtr> rects(10);
+ for (size_t i = 0; i < rects.size(); ++i) {
+ RedmondRect rr = kBase;
+ rr.left += static_cast<int32_t>(i);
+ rr.top += static_cast<int32_t>(i);
+ rects[i] = Rect::From(rr);
+ }
+
+ std::vector<RedmondRect> redmond_rects = rects.To<std::vector<RedmondRect>>();
+
+ Array<RectPtr> rects2 = Array<RectPtr>::From(redmond_rects);
+ EXPECT_TRUE(AreEqualRectArrays(rects, rects2));
+}
+
+TEST(TypeConversionTest, CustomTypeConverter_Nested) {
+ RedmondNamedRegion redmond_region;
+ redmond_region.name = "foopy";
+
+ const RedmondRect kBase = {10, 20, 30, 40};
+
+ for (size_t i = 0; i < 10; ++i) {
+ RedmondRect rect = kBase;
+ rect.left += static_cast<int32_t>(i);
+ rect.top += static_cast<int32_t>(i);
+ redmond_region.rects.push_back(rect);
+ }
+
+ // Round-trip through generated struct and TypeConverter.
+
+ NamedRegionPtr copy = NamedRegion::From(redmond_region);
+ RedmondNamedRegion redmond_region2 = copy.To<RedmondNamedRegion>();
+
+ EXPECT_EQ(redmond_region.name, redmond_region2.name);
+ EXPECT_EQ(redmond_region.rects.size(), redmond_region2.rects.size());
+ for (size_t i = 0; i < redmond_region.rects.size(); ++i) {
+ EXPECT_EQ(redmond_region.rects[i].left, redmond_region2.rects[i].left);
+ EXPECT_EQ(redmond_region.rects[i].top, redmond_region2.rects[i].top);
+ EXPECT_EQ(redmond_region.rects[i].right, redmond_region2.rects[i].right);
+ EXPECT_EQ(redmond_region.rects[i].bottom, redmond_region2.rects[i].bottom);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
new file mode 100644
index 0000000..e6fddb7
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -0,0 +1,1131 @@
+// Copyright 2014 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 <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+
+TEST(UnionTest, PlainOldDataGetterSetter) {
+ PodUnionPtr pod(PodUnion::New());
+
+ pod->set_f_int8(10);
+ EXPECT_EQ(10, pod->get_f_int8());
+ EXPECT_TRUE(pod->is_f_int8());
+ EXPECT_FALSE(pod->is_f_int8_other());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT8);
+
+ pod->set_f_uint8(11);
+ EXPECT_EQ(11, pod->get_f_uint8());
+ EXPECT_TRUE(pod->is_f_uint8());
+ EXPECT_FALSE(pod->is_f_int8());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT8);
+
+ pod->set_f_int16(12);
+ EXPECT_EQ(12, pod->get_f_int16());
+ EXPECT_TRUE(pod->is_f_int16());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT16);
+
+ pod->set_f_uint16(13);
+ EXPECT_EQ(13, pod->get_f_uint16());
+ EXPECT_TRUE(pod->is_f_uint16());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT16);
+
+ pod->set_f_int32(14);
+ EXPECT_EQ(14, pod->get_f_int32());
+ EXPECT_TRUE(pod->is_f_int32());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT32);
+
+ pod->set_f_uint32(static_cast<uint32_t>(15));
+ EXPECT_EQ(static_cast<uint32_t>(15), pod->get_f_uint32());
+ EXPECT_TRUE(pod->is_f_uint32());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT32);
+
+ pod->set_f_int64(16);
+ EXPECT_EQ(16, pod->get_f_int64());
+ EXPECT_TRUE(pod->is_f_int64());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT64);
+
+ pod->set_f_uint64(static_cast<uint64_t>(17));
+ EXPECT_EQ(static_cast<uint64_t>(17), pod->get_f_uint64());
+ EXPECT_TRUE(pod->is_f_uint64());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT64);
+
+ pod->set_f_float(1.5);
+ EXPECT_EQ(1.5, pod->get_f_float());
+ EXPECT_TRUE(pod->is_f_float());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_FLOAT);
+
+ pod->set_f_double(1.9);
+ EXPECT_EQ(1.9, pod->get_f_double());
+ EXPECT_TRUE(pod->is_f_double());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_DOUBLE);
+
+ pod->set_f_bool(true);
+ EXPECT_TRUE(pod->get_f_bool());
+ pod->set_f_bool(false);
+ EXPECT_FALSE(pod->get_f_bool());
+ EXPECT_TRUE(pod->is_f_bool());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_BOOL);
+
+ pod->set_f_enum(AN_ENUM_SECOND);
+ EXPECT_EQ(AN_ENUM_SECOND, pod->get_f_enum());
+ EXPECT_TRUE(pod->is_f_enum());
+ EXPECT_EQ(pod->which(), PodUnion::Tag::F_ENUM);
+}
+
+TEST(UnionTest, PodEquals) {
+ PodUnionPtr pod1(PodUnion::New());
+ PodUnionPtr pod2(PodUnion::New());
+
+ pod1->set_f_int8(10);
+ pod2->set_f_int8(10);
+ EXPECT_TRUE(pod1.Equals(pod2));
+
+ pod2->set_f_int8(11);
+ EXPECT_FALSE(pod1.Equals(pod2));
+
+ pod2->set_f_int8_other(10);
+ EXPECT_FALSE(pod1.Equals(pod2));
+}
+
+TEST(UnionTest, PodClone) {
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int8(10);
+
+ PodUnionPtr pod_clone = pod.Clone();
+ EXPECT_EQ(10, pod_clone->get_f_int8());
+ EXPECT_TRUE(pod_clone->is_f_int8());
+ EXPECT_EQ(pod_clone->which(), PodUnion::Tag::F_INT8);
+}
+
+TEST(UnionTest, PodSerialization) {
+ PodUnionPtr pod1(PodUnion::New());
+ pod1->set_f_int8(10);
+
+ size_t size = GetSerializedSize_(pod1, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = nullptr;
+ SerializeUnion_(pod1.Pass(), &buf, &data, false);
+
+ PodUnionPtr pod2;
+ Deserialize_(data, &pod2);
+
+ EXPECT_EQ(10, pod2->get_f_int8());
+ EXPECT_TRUE(pod2->is_f_int8());
+ EXPECT_EQ(pod2->which(), PodUnion::Tag::F_INT8);
+}
+
+TEST(UnionTest, EnumSerialization) {
+ PodUnionPtr pod1(PodUnion::New());
+ pod1->set_f_enum(AN_ENUM_SECOND);
+
+ size_t size = GetSerializedSize_(pod1, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = nullptr;
+ SerializeUnion_(pod1.Pass(), &buf, &data, false);
+
+ PodUnionPtr pod2;
+ Deserialize_(data, &pod2);
+
+ EXPECT_EQ(AN_ENUM_SECOND, pod2->get_f_enum());
+ EXPECT_TRUE(pod2->is_f_enum());
+ EXPECT_EQ(pod2->which(), PodUnion::Tag::F_ENUM);
+}
+
+TEST(UnionTest, PodValidation) {
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int8(10);
+
+ size_t size = GetSerializedSize_(pod, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = nullptr;
+ SerializeUnion_(pod.Pass(), &buf, &data, false);
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_TRUE(
+ internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, SerializeNotNull) {
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int8(0);
+ size_t size = GetSerializedSize_(pod, false);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = nullptr;
+ SerializeUnion_(pod.Pass(), &buf, &data, false);
+ EXPECT_FALSE(data->is_null());
+}
+
+TEST(UnionTest, SerializeIsNullInlined) {
+ PodUnionPtr pod;
+ size_t size = GetSerializedSize_(pod, false);
+ EXPECT_EQ(16U, size);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
+
+ // Check that dirty output buffers are handled correctly by serialization.
+ data->size = 16U;
+ data->tag = PodUnion::Tag::F_UINT16;
+ data->data.f_f_int16 = 20;
+
+ SerializeUnion_(pod.Pass(), &buf, &data, true);
+ EXPECT_TRUE(data->is_null());
+
+ PodUnionPtr pod2;
+ Deserialize_(data, &pod2);
+ EXPECT_TRUE(pod2.is_null());
+}
+
+TEST(UnionTest, SerializeIsNullNotInlined) {
+ PodUnionPtr pod;
+ size_t size = GetSerializedSize_(pod, false);
+ EXPECT_EQ(16U, size);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = nullptr;
+ SerializeUnion_(pod.Pass(), &buf, &data, false);
+ EXPECT_EQ(nullptr, data);
+}
+
+TEST(UnionTest, NullValidation) {
+ void* buf = nullptr;
+ mojo::internal::BoundsChecker bounds_checker(buf, 0, 0);
+ EXPECT_TRUE(internal::PodUnion_Data::Validate(buf, &bounds_checker, false));
+}
+
+TEST(UnionTest, OutOfAlignmentValidation) {
+ size_t size = sizeof(internal::PodUnion_Data);
+ // Get an aligned object and shift the alignment.
+ mojo::internal::FixedBufferForTesting aligned_buf(size + 1);
+ void* raw_buf = aligned_buf.Leak();
+ char* buf = reinterpret_cast<char*>(raw_buf) + 1;
+
+ internal::PodUnion_Data* data =
+ reinterpret_cast<internal::PodUnion_Data*>(buf);
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_FALSE(internal::PodUnion_Data::Validate(buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, OOBValidation) {
+ size_t size = sizeof(internal::PodUnion_Data) - 1;
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ void* raw_buf = buf.Leak();
+ EXPECT_FALSE(
+ internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, UnknownTagValidation) {
+ size_t size = sizeof(internal::PodUnion_Data);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
+ data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF);
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ void* raw_buf = buf.Leak();
+ EXPECT_FALSE(
+ internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, StringGetterSetter) {
+ ObjectUnionPtr pod(ObjectUnion::New());
+
+ String hello("hello world");
+ pod->set_f_string(hello);
+ EXPECT_EQ(hello, pod->get_f_string());
+ EXPECT_TRUE(pod->is_f_string());
+ EXPECT_EQ(pod->which(), ObjectUnion::Tag::F_STRING);
+}
+
+TEST(UnionTest, StringEquals) {
+ ObjectUnionPtr pod1(ObjectUnion::New());
+ ObjectUnionPtr pod2(ObjectUnion::New());
+
+ pod1->set_f_string("hello world");
+ pod2->set_f_string("hello world");
+ EXPECT_TRUE(pod1.Equals(pod2));
+
+ pod2->set_f_string("hello universe");
+ EXPECT_FALSE(pod1.Equals(pod2));
+}
+
+TEST(UnionTest, StringClone) {
+ ObjectUnionPtr pod(ObjectUnion::New());
+
+ String hello("hello world");
+ pod->set_f_string(hello);
+ ObjectUnionPtr pod_clone = pod.Clone();
+ EXPECT_EQ(hello, pod_clone->get_f_string());
+ EXPECT_TRUE(pod_clone->is_f_string());
+ EXPECT_EQ(pod_clone->which(), ObjectUnion::Tag::F_STRING);
+}
+
+TEST(UnionTest, StringSerialization) {
+ ObjectUnionPtr pod1(ObjectUnion::New());
+
+ String hello("hello world");
+ pod1->set_f_string(hello);
+
+ size_t size = GetSerializedSize_(pod1, false);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(pod1.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ data->DecodePointersAndHandles(&handles);
+
+ ObjectUnionPtr pod2;
+ Deserialize_(data, &pod2);
+ EXPECT_EQ(hello, pod2->get_f_string());
+ EXPECT_TRUE(pod2->is_f_string());
+ EXPECT_EQ(pod2->which(), ObjectUnion::Tag::F_STRING);
+}
+
+TEST(UnionTest, NullStringValidation) {
+ size_t size = sizeof(internal::ObjectUnion_Data);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf);
+ data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
+ data->data.unknown = 0x0;
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ void* raw_buf = buf.Leak();
+ EXPECT_FALSE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, StringPointerOverflowValidation) {
+ size_t size = sizeof(internal::ObjectUnion_Data);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf);
+ data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
+ data->data.unknown = 0xFFFFFFFFFFFFFFFF;
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ void* raw_buf = buf.Leak();
+ EXPECT_FALSE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, StringValidateOOB) {
+ size_t size = 32;
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf);
+ data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
+
+ data->data.f_f_string.offset = 8;
+ char* ptr = reinterpret_cast<char*>(&data->data.f_f_string);
+ mojo::internal::ArrayHeader* array_header =
+ reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr);
+ array_header->num_bytes = 20; // This should go out of bounds.
+ array_header->num_elements = 20;
+ mojo::internal::BoundsChecker bounds_checker(data, 32, 0);
+ void* raw_buf = buf.Leak();
+ EXPECT_FALSE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+// TODO(azani): Move back in array_unittest.cc when possible.
+// Array tests
+TEST(UnionTest, PodUnionInArray) {
+ SmallStructPtr small_struct(SmallStruct::New());
+ small_struct->pod_union_array = Array<PodUnionPtr>(2);
+ small_struct->pod_union_array[0] = PodUnion::New();
+ small_struct->pod_union_array[1] = PodUnion::New();
+
+ small_struct->pod_union_array[0]->set_f_int8(10);
+ small_struct->pod_union_array[1]->set_f_int16(12);
+
+ EXPECT_EQ(10, small_struct->pod_union_array[0]->get_f_int8());
+ EXPECT_EQ(12, small_struct->pod_union_array[1]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInArraySerialization) {
+ Array<PodUnionPtr> array(2);
+ array[0] = PodUnion::New();
+ array[1] = PodUnion::New();
+
+ array[0]->set_f_int8(10);
+ array[1]->set_f_int16(12);
+ EXPECT_EQ(2U, array.size());
+
+ size_t size = GetSerializedSize_(array);
+ EXPECT_EQ(40U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ mojo::internal::Array_Data<internal::PodUnion_Data>* data;
+ mojo::internal::ArrayValidateParams validate_params(0, false, nullptr);
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<PodUnionPtr> array2;
+ Deserialize_(data, &array2);
+
+ EXPECT_EQ(2U, array2.size());
+
+ EXPECT_EQ(10, array2[0]->get_f_int8());
+ EXPECT_EQ(12, array2[1]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInArraySerializationWithNull) {
+ Array<PodUnionPtr> array(2);
+ array[0] = PodUnion::New();
+
+ array[0]->set_f_int8(10);
+ EXPECT_EQ(2U, array.size());
+
+ size_t size = GetSerializedSize_(array);
+ EXPECT_EQ(40U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ mojo::internal::Array_Data<internal::PodUnion_Data>* data;
+ mojo::internal::ArrayValidateParams validate_params(0, true, nullptr);
+ SerializeArray_(array.Pass(), &buf, &data, &validate_params);
+
+ Array<PodUnionPtr> array2;
+ Deserialize_(data, &array2);
+
+ EXPECT_EQ(2U, array2.size());
+
+ EXPECT_EQ(10, array2[0]->get_f_int8());
+ EXPECT_TRUE(array2[1].is_null());
+}
+
+// TODO(azani): Move back in struct_unittest.cc when possible.
+// Struct tests
+TEST(UnionTest, Clone_Union) {
+ SmallStructPtr small_struct(SmallStruct::New());
+ small_struct->pod_union = PodUnion::New();
+ small_struct->pod_union->set_f_int8(10);
+
+ SmallStructPtr clone = small_struct.Clone();
+ EXPECT_EQ(10, clone->pod_union->get_f_int8());
+}
+
+// Serialization test of a struct with a union of plain old data.
+TEST(UnionTest, Serialization_UnionOfPods) {
+ SmallStructPtr small_struct(SmallStruct::New());
+ small_struct->pod_union = PodUnion::New();
+ small_struct->pod_union->set_f_int32(10);
+
+ size_t size = GetSerializedSize_(small_struct);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::SmallStruct_Data* data = nullptr;
+ Serialize_(small_struct.Pass(), &buf, &data);
+
+ SmallStructPtr deserialized;
+ Deserialize_(data, &deserialized);
+
+ EXPECT_EQ(10, deserialized->pod_union->get_f_int32());
+}
+
+// Serialization test of a struct with a union of structs.
+TEST(UnionTest, Serialization_UnionOfObjects) {
+ SmallObjStructPtr obj_struct(SmallObjStruct::New());
+ obj_struct->obj_union = ObjectUnion::New();
+ String hello("hello world");
+ obj_struct->obj_union->set_f_string(hello);
+
+ size_t size = GetSerializedSize_(obj_struct);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::SmallObjStruct_Data* data = nullptr;
+ Serialize_(obj_struct.Pass(), &buf, &data);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ data->DecodePointersAndHandles(&handles);
+
+ SmallObjStructPtr deserialized;
+ Deserialize_(data, &deserialized);
+
+ EXPECT_EQ(hello, deserialized->obj_union->get_f_string());
+}
+
+// Validation test of a struct with a union.
+TEST(UnionTest, Validation_UnionsInStruct) {
+ SmallStructPtr small_struct(SmallStruct::New());
+ small_struct->pod_union = PodUnion::New();
+ small_struct->pod_union->set_f_int32(10);
+
+ size_t size = GetSerializedSize_(small_struct);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::SmallStruct_Data* data = nullptr;
+ Serialize_(small_struct.Pass(), &buf, &data);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_TRUE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker));
+ free(raw_buf);
+}
+
+// Validation test of a struct union fails due to unknown union tag.
+TEST(UnionTest, Validation_PodUnionInStruct_Failure) {
+ SmallStructPtr small_struct(SmallStruct::New());
+ small_struct->pod_union = PodUnion::New();
+ small_struct->pod_union->set_f_int32(10);
+
+ size_t size = GetSerializedSize_(small_struct);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::SmallStruct_Data* data = nullptr;
+ Serialize_(small_struct.Pass(), &buf, &data);
+ data->pod_union.tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(100);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_FALSE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker));
+ free(raw_buf);
+}
+
+// Validation fails due to non-nullable null union in struct.
+TEST(UnionTest, Validation_NullUnion_Failure) {
+ SmallStructNonNullableUnionPtr small_struct(
+ SmallStructNonNullableUnion::New());
+
+ size_t size = GetSerializedSize_(small_struct);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::SmallStructNonNullableUnion_Data* data =
+ internal::SmallStructNonNullableUnion_Data::New(&buf);
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate(
+ raw_buf, &bounds_checker));
+ free(raw_buf);
+}
+
+// Validation passes with nullable null union.
+TEST(UnionTest, Validation_NullableUnion) {
+ SmallStructPtr small_struct(SmallStruct::New());
+
+ size_t size = GetSerializedSize_(small_struct);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::SmallStruct_Data* data = nullptr;
+ Serialize_(small_struct.Pass(), &buf, &data);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_TRUE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker));
+ free(raw_buf);
+}
+
+// TODO(azani): Move back in map_unittest.cc when possible.
+// Map Tests
+TEST(UnionTest, PodUnionInMap) {
+ SmallStructPtr small_struct(SmallStruct::New());
+ small_struct->pod_union_map = Map<String, PodUnionPtr>();
+ small_struct->pod_union_map.insert("one", PodUnion::New());
+ small_struct->pod_union_map.insert("two", PodUnion::New());
+
+ small_struct->pod_union_map["one"]->set_f_int8(8);
+ small_struct->pod_union_map["two"]->set_f_int16(16);
+
+ EXPECT_EQ(8, small_struct->pod_union_map["one"]->get_f_int8());
+ EXPECT_EQ(16, small_struct->pod_union_map["two"]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInMapSerialization) {
+ Map<String, PodUnionPtr> map;
+ map.insert("one", PodUnion::New());
+ map.insert("two", PodUnion::New());
+
+ map["one"]->set_f_int8(8);
+ map["two"]->set_f_int16(16);
+
+ size_t size = GetSerializedSize_(map);
+ EXPECT_EQ(120U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ mojo::internal::Map_Data<mojo::internal::String_Data*,
+ internal::PodUnion_Data>* data;
+ mojo::internal::ArrayValidateParams validate_params(0, false, nullptr);
+ SerializeMap_(map.Pass(), &buf, &data, &validate_params);
+
+ Map<String, PodUnionPtr> map2;
+ Deserialize_(data, &map2);
+
+ EXPECT_EQ(8, map2["one"]->get_f_int8());
+ EXPECT_EQ(16, map2["two"]->get_f_int16());
+}
+
+TEST(UnionTest, PodUnionInMapSerializationWithNull) {
+ Map<String, PodUnionPtr> map;
+ map.insert("one", PodUnion::New());
+ map.insert("two", nullptr);
+
+ map["one"]->set_f_int8(8);
+
+ size_t size = GetSerializedSize_(map);
+ EXPECT_EQ(120U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ mojo::internal::Map_Data<mojo::internal::String_Data*,
+ internal::PodUnion_Data>* data;
+ mojo::internal::ArrayValidateParams validate_params(0, true, nullptr);
+ SerializeMap_(map.Pass(), &buf, &data, &validate_params);
+
+ Map<String, PodUnionPtr> map2;
+ Deserialize_(data, &map2);
+
+ EXPECT_EQ(8, map2["one"]->get_f_int8());
+ EXPECT_TRUE(map2["two"].is_null());
+}
+
+TEST(UnionTest, StructInUnionGetterSetterPasser) {
+ DummyStructPtr dummy(DummyStruct::New());
+ dummy->f_int8 = 8;
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_dummy(dummy.Pass());
+
+ EXPECT_EQ(8, obj->get_f_dummy()->f_int8);
+}
+
+TEST(UnionTest, StructInUnionSerialization) {
+ DummyStructPtr dummy(DummyStruct::New());
+ dummy->f_int8 = 8;
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_dummy(dummy.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ EXPECT_EQ(32U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ data->DecodePointersAndHandles(&handles);
+
+ ObjectUnionPtr obj2;
+ Deserialize_(data, &obj2);
+ EXPECT_EQ(8, obj2->get_f_dummy()->f_int8);
+}
+
+TEST(UnionTest, StructInUnionValidation) {
+ DummyStructPtr dummy(DummyStruct::New());
+ dummy->f_int8 = 8;
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_dummy(dummy.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_TRUE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, StructInUnionValidationNonNullable) {
+ DummyStructPtr dummy(nullptr);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_dummy(dummy.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_FALSE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, StructInUnionValidationNullable) {
+ DummyStructPtr dummy(nullptr);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_nullable(dummy.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_TRUE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, ArrayInUnionGetterSetter) {
+ Array<int8_t> array(2);
+ array[0] = 8;
+ array[1] = 9;
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_array_int8(array.Pass());
+
+ EXPECT_EQ(8, obj->get_f_array_int8()[0]);
+ EXPECT_EQ(9, obj->get_f_array_int8()[1]);
+}
+
+TEST(UnionTest, ArrayInUnionSerialization) {
+ Array<int8_t> array(2);
+ array[0] = 8;
+ array[1] = 9;
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_array_int8(array.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ EXPECT_EQ(32U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ data->DecodePointersAndHandles(&handles);
+
+ ObjectUnionPtr obj2;
+ Deserialize_(data, &obj2);
+
+ EXPECT_EQ(8, obj2->get_f_array_int8()[0]);
+ EXPECT_EQ(9, obj2->get_f_array_int8()[1]);
+}
+
+TEST(UnionTest, ArrayInUnionValidation) {
+ Array<int8_t> array(2);
+ array[0] = 8;
+ array[1] = 9;
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_array_int8(array.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+
+ EXPECT_TRUE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, MapInUnionGetterSetter) {
+ Map<String, int8_t> map;
+ map.insert("one", 1);
+ map.insert("two", 2);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_map_int8(map.Pass());
+
+ EXPECT_EQ(1, obj->get_f_map_int8()["one"]);
+ EXPECT_EQ(2, obj->get_f_map_int8()["two"]);
+}
+
+TEST(UnionTest, MapInUnionSerialization) {
+ Map<String, int8_t> map;
+ map.insert("one", 1);
+ map.insert("two", 2);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_map_int8(map.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ EXPECT_EQ(112U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ data->DecodePointersAndHandles(&handles);
+
+ ObjectUnionPtr obj2;
+ Deserialize_(data, &obj2);
+
+ EXPECT_EQ(1, obj2->get_f_map_int8()["one"]);
+ EXPECT_EQ(2, obj2->get_f_map_int8()["two"]);
+}
+
+TEST(UnionTest, MapInUnionValidation) {
+ Map<String, int8_t> map;
+ map.insert("one", 1);
+ map.insert("two", 2);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_map_int8(map.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ EXPECT_EQ(112U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+
+ EXPECT_TRUE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, UnionInUnionGetterSetter) {
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int8(10);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_pod_union(pod.Pass());
+
+ EXPECT_EQ(10, obj->get_f_pod_union()->get_f_int8());
+}
+
+TEST(UnionTest, UnionInUnionSerialization) {
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int8(10);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_pod_union(pod.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ EXPECT_EQ(32U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ data->DecodePointersAndHandles(&handles);
+
+ ObjectUnionPtr obj2;
+ Deserialize_(data, &obj2);
+ EXPECT_EQ(10, obj2->get_f_pod_union()->get_f_int8());
+}
+
+TEST(UnionTest, UnionInUnionValidation) {
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int8(10);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_pod_union(pod.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+ EXPECT_EQ(32U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_TRUE(handles.empty());
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_TRUE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, UnionInUnionValidationNonNullable) {
+ PodUnionPtr pod(nullptr);
+
+ ObjectUnionPtr obj(ObjectUnion::New());
+ obj->set_f_pod_union(pod.Pass());
+
+ size_t size = GetSerializedSize_(obj, false);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::ObjectUnion_Data* data = nullptr;
+ SerializeUnion_(obj.Pass(), &buf, &data, false);
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 0);
+ EXPECT_FALSE(
+ internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, HandleInUnionGetterSetter) {
+ ScopedMessagePipeHandle pipe0;
+ ScopedMessagePipeHandle pipe1;
+
+ CreateMessagePipe(nullptr, &pipe0, &pipe1);
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_message_pipe(pipe1.Pass());
+
+ std::string golden("hello world");
+ WriteTextMessage(pipe0.get(), golden);
+
+ std::string actual;
+ ReadTextMessage(handle->get_f_message_pipe().get(), &actual);
+
+ EXPECT_EQ(golden, actual);
+}
+
+TEST(UnionTest, HandleInUnionSerialization) {
+ ScopedMessagePipeHandle pipe0;
+ ScopedMessagePipeHandle pipe1;
+
+ CreateMessagePipe(nullptr, &pipe0, &pipe1);
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_message_pipe(pipe1.Pass());
+
+ size_t size = GetSerializedSize_(handle, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::HandleUnion_Data* data = nullptr;
+ SerializeUnion_(handle.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_EQ(1U, handles.size());
+ data->DecodePointersAndHandles(&handles);
+
+ HandleUnionPtr handle2(HandleUnion::New());
+ Deserialize_(data, &handle2);
+
+ std::string golden("hello world");
+ WriteTextMessage(pipe0.get(), golden);
+
+ std::string actual;
+ ReadTextMessage(handle2->get_f_message_pipe().get(), &actual);
+
+ EXPECT_EQ(golden, actual);
+}
+
+TEST(UnionTest, HandleInUnionValidation) {
+ ScopedMessagePipeHandle pipe0;
+ ScopedMessagePipeHandle pipe1;
+
+ CreateMessagePipe(nullptr, &pipe0, &pipe1);
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_message_pipe(pipe1.Pass());
+
+ size_t size = GetSerializedSize_(handle, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::HandleUnion_Data* data = nullptr;
+ SerializeUnion_(handle.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 1);
+ EXPECT_TRUE(
+ internal::HandleUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+TEST(UnionTest, HandleInUnionValidationNull) {
+ ScopedMessagePipeHandle pipe;
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_message_pipe(pipe.Pass());
+
+ size_t size = GetSerializedSize_(handle, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::HandleUnion_Data* data = nullptr;
+ SerializeUnion_(handle.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+
+ void* raw_buf = buf.Leak();
+ mojo::internal::BoundsChecker bounds_checker(data,
+ static_cast<uint32_t>(size), 1);
+ EXPECT_FALSE(
+ internal::HandleUnion_Data::Validate(raw_buf, &bounds_checker, false));
+ free(raw_buf);
+}
+
+class SmallCacheImpl : public SmallCache {
+ public:
+ SmallCacheImpl() : int_value_(0) {}
+ ~SmallCacheImpl() override {}
+ int64_t int_value() const { return int_value_; }
+
+ private:
+ void SetIntValue(int64_t int_value) override { int_value_ = int_value; }
+ void GetIntValue(const GetIntValueCallback& callback) override {
+ callback.Run(int_value_);
+ }
+
+ int64_t int_value_;
+};
+
+TEST(UnionTest, InterfaceInUnion) {
+ base::MessageLoop run_loop(common::MessagePumpMojo::Create());
+ SmallCacheImpl impl;
+ SmallCachePtr ptr;
+ Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_small_cache(ptr.Pass());
+
+ handle->get_f_small_cache()->SetIntValue(10);
+ run_loop.RunUntilIdle();
+ EXPECT_EQ(10, impl.int_value());
+}
+
+TEST(UnionTest, InterfaceInUnionSerialization) {
+ base::MessageLoop run_loop(common::MessagePumpMojo::Create());
+ SmallCacheImpl impl;
+ SmallCachePtr ptr;
+ Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+
+ HandleUnionPtr handle(HandleUnion::New());
+ handle->set_f_small_cache(ptr.Pass());
+ size_t size = GetSerializedSize_(handle, false);
+ EXPECT_EQ(16U, size);
+
+ mojo::internal::FixedBufferForTesting buf(size);
+ internal::HandleUnion_Data* data = nullptr;
+ SerializeUnion_(handle.Pass(), &buf, &data, false);
+
+ std::vector<Handle> handles;
+ data->EncodePointersAndHandles(&handles);
+ EXPECT_EQ(1U, handles.size());
+ data->DecodePointersAndHandles(&handles);
+
+ HandleUnionPtr handle2(HandleUnion::New());
+ Deserialize_(data, &handle2);
+
+ handle2->get_f_small_cache()->SetIntValue(10);
+ run_loop.RunUntilIdle();
+ EXPECT_EQ(10, impl.int_value());
+}
+
+class UnionInterfaceImpl : public UnionInterface {
+ public:
+ UnionInterfaceImpl() {}
+ ~UnionInterfaceImpl() override {}
+
+ private:
+ void Echo(PodUnionPtr in, const EchoCallback& callback) override {
+ callback.Run(in.Pass());
+ }
+};
+
+TEST(UnionTest, UnionInInterface) {
+ base::MessageLoop run_loop(common::MessagePumpMojo::Create());
+ UnionInterfaceImpl impl;
+ UnionInterfacePtr ptr;
+ Binding<UnionInterface> bindings(&impl, GetProxy(&ptr));
+
+ PodUnionPtr pod(PodUnion::New());
+ pod->set_f_int16(16);
+
+ ptr->Echo(pod.Pass(),
+ [](PodUnionPtr out) { EXPECT_EQ(16, out->get_f_int16()); });
+ run_loop.RunUntilIdle();
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
new file mode 100644
index 0000000..9d2607d
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
@@ -0,0 +1,410 @@
+// Copyright 2014 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/tests/validation_test_input_parser.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <limits>
+#include <map>
+#include <set>
+#include <utility>
+
+#include "mojo/public/c/system/macros.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class ValidationTestInputParser {
+ public:
+ ValidationTestInputParser(const std::string& input,
+ std::vector<uint8_t>* data,
+ size_t* num_handles,
+ std::string* error_message);
+ ~ValidationTestInputParser();
+
+ bool Run();
+
+ private:
+ struct DataType;
+
+ typedef std::pair<const char*, const char*> Range;
+
+ typedef bool (ValidationTestInputParser::*ParseDataFunc)(
+ const DataType& type,
+ const std::string& value_string);
+
+ struct DataType {
+ const char* name;
+ size_t name_size;
+ size_t data_size;
+ ParseDataFunc parse_data_func;
+ };
+
+ // A dist4/8 item that hasn't been matched with an anchr item.
+ struct PendingDistanceItem {
+ // Where this data item is located in |data_|.
+ size_t pos;
+ // Either 4 or 8 (bytes).
+ size_t data_size;
+ };
+
+ bool GetNextItem(Range* range);
+
+ bool ParseItem(const Range& range);
+
+ bool ParseUnsignedInteger(const DataType& type,
+ const std::string& value_string);
+ bool ParseSignedInteger(const DataType& type,
+ const std::string& value_string);
+ bool ParseFloat(const DataType& type, const std::string& value_string);
+ bool ParseDouble(const DataType& type, const std::string& value_string);
+ bool ParseBinarySequence(const DataType& type,
+ const std::string& value_string);
+ bool ParseDistance(const DataType& type, const std::string& value_string);
+ bool ParseAnchor(const DataType& type, const std::string& value_string);
+ bool ParseHandles(const DataType& type, const std::string& value_string);
+
+ bool StartsWith(const Range& range, const char* prefix, size_t prefix_length);
+
+ bool ConvertToUnsignedInteger(const std::string& value_string,
+ unsigned long long int* value);
+
+ template <typename T>
+ void AppendData(T data) {
+ size_t pos = data_->size();
+ data_->resize(pos + sizeof(T));
+ memcpy(&(*data_)[pos], &data, sizeof(T));
+ }
+
+ template <typename TargetType, typename InputType>
+ bool ConvertAndAppendData(InputType value) {
+ if (value > std::numeric_limits<TargetType>::max() ||
+ value < std::numeric_limits<TargetType>::min()) {
+ return false;
+ }
+ AppendData(static_cast<TargetType>(value));
+ return true;
+ }
+
+ template <typename TargetType, typename InputType>
+ bool ConvertAndFillData(size_t pos, InputType value) {
+ if (value > std::numeric_limits<TargetType>::max() ||
+ value < std::numeric_limits<TargetType>::min()) {
+ return false;
+ }
+ TargetType target_value = static_cast<TargetType>(value);
+ assert(pos + sizeof(TargetType) <= data_->size());
+ memcpy(&(*data_)[pos], &target_value, sizeof(TargetType));
+ return true;
+ }
+
+ static const DataType kDataTypes[];
+ static const size_t kDataTypeCount;
+
+ const std::string& input_;
+ size_t input_cursor_;
+
+ std::vector<uint8_t>* data_;
+ size_t* num_handles_;
+ std::string* error_message_;
+
+ std::map<std::string, PendingDistanceItem> pending_distance_items_;
+ std::set<std::string> anchors_;
+};
+
+#define DATA_TYPE(name, data_size, parse_data_func) \
+ { name, sizeof(name) - 1, data_size, parse_data_func }
+
+const ValidationTestInputParser::DataType
+ ValidationTestInputParser::kDataTypes[] = {
+ DATA_TYPE("[u1]", 1, &ValidationTestInputParser::ParseUnsignedInteger),
+ DATA_TYPE("[u2]", 2, &ValidationTestInputParser::ParseUnsignedInteger),
+ DATA_TYPE("[u4]", 4, &ValidationTestInputParser::ParseUnsignedInteger),
+ DATA_TYPE("[u8]", 8, &ValidationTestInputParser::ParseUnsignedInteger),
+ DATA_TYPE("[s1]", 1, &ValidationTestInputParser::ParseSignedInteger),
+ DATA_TYPE("[s2]", 2, &ValidationTestInputParser::ParseSignedInteger),
+ DATA_TYPE("[s4]", 4, &ValidationTestInputParser::ParseSignedInteger),
+ DATA_TYPE("[s8]", 8, &ValidationTestInputParser::ParseSignedInteger),
+ DATA_TYPE("[b]", 1, &ValidationTestInputParser::ParseBinarySequence),
+ DATA_TYPE("[f]", 4, &ValidationTestInputParser::ParseFloat),
+ DATA_TYPE("[d]", 8, &ValidationTestInputParser::ParseDouble),
+ DATA_TYPE("[dist4]", 4, &ValidationTestInputParser::ParseDistance),
+ DATA_TYPE("[dist8]", 8, &ValidationTestInputParser::ParseDistance),
+ DATA_TYPE("[anchr]", 0, &ValidationTestInputParser::ParseAnchor),
+ DATA_TYPE("[handles]", 0, &ValidationTestInputParser::ParseHandles)};
+
+const size_t ValidationTestInputParser::kDataTypeCount =
+ sizeof(ValidationTestInputParser::kDataTypes) /
+ sizeof(ValidationTestInputParser::kDataTypes[0]);
+
+ValidationTestInputParser::ValidationTestInputParser(const std::string& input,
+ std::vector<uint8_t>* data,
+ size_t* num_handles,
+ std::string* error_message)
+ : input_(input),
+ input_cursor_(0),
+ data_(data),
+ num_handles_(num_handles),
+ error_message_(error_message) {
+ assert(data_);
+ assert(num_handles_);
+ assert(error_message_);
+ data_->clear();
+ *num_handles_ = 0;
+ error_message_->clear();
+}
+
+ValidationTestInputParser::~ValidationTestInputParser() {
+}
+
+bool ValidationTestInputParser::Run() {
+ Range range;
+ bool result = true;
+ while (result && GetNextItem(&range))
+ result = ParseItem(range);
+
+ if (!result) {
+ *error_message_ =
+ "Error occurred when parsing " + std::string(range.first, range.second);
+ } else if (!pending_distance_items_.empty()) {
+ // We have parsed all the contents in |input_| successfully, but there are
+ // unmatched dist4/8 items.
+ *error_message_ = "Error occurred when matching [dist4/8] and [anchr].";
+ result = false;
+ }
+ if (!result) {
+ data_->clear();
+ *num_handles_ = 0;
+ } else {
+ assert(error_message_->empty());
+ }
+
+ return result;
+}
+
+bool ValidationTestInputParser::GetNextItem(Range* range) {
+ const char kWhitespaceChars[] = " \t\n\r";
+ const char kItemDelimiters[] = " \t\n\r/";
+ const char kEndOfLineChars[] = "\n\r";
+ while (true) {
+ // Skip leading whitespaces.
+ // If there are no non-whitespace characters left, |input_cursor_| will be
+ // set to std::npos.
+ input_cursor_ = input_.find_first_not_of(kWhitespaceChars, input_cursor_);
+
+ if (input_cursor_ >= input_.size())
+ return false;
+
+ if (StartsWith(
+ Range(&input_[0] + input_cursor_, &input_[0] + input_.size()),
+ "//",
+ 2)) {
+ // Skip contents until the end of the line.
+ input_cursor_ = input_.find_first_of(kEndOfLineChars, input_cursor_);
+ } else {
+ range->first = &input_[0] + input_cursor_;
+ input_cursor_ = input_.find_first_of(kItemDelimiters, input_cursor_);
+ range->second = input_cursor_ >= input_.size()
+ ? &input_[0] + input_.size()
+ : &input_[0] + input_cursor_;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ValidationTestInputParser::ParseItem(const Range& range) {
+ for (size_t i = 0; i < kDataTypeCount; ++i) {
+ if (StartsWith(range, kDataTypes[i].name, kDataTypes[i].name_size)) {
+ return (this->*kDataTypes[i].parse_data_func)(
+ kDataTypes[i],
+ std::string(range.first + kDataTypes[i].name_size, range.second));
+ }
+ }
+
+ // "[u1]" is optional.
+ return ParseUnsignedInteger(kDataTypes[0],
+ std::string(range.first, range.second));
+}
+
+bool ValidationTestInputParser::ParseUnsignedInteger(
+ const DataType& type,
+ const std::string& value_string) {
+ unsigned long long int value;
+ if (!ConvertToUnsignedInteger(value_string, &value))
+ return false;
+
+ switch (type.data_size) {
+ case 1:
+ return ConvertAndAppendData<uint8_t>(value);
+ case 2:
+ return ConvertAndAppendData<uint16_t>(value);
+ case 4:
+ return ConvertAndAppendData<uint32_t>(value);
+ case 8:
+ return ConvertAndAppendData<uint64_t>(value);
+ default:
+ assert(false);
+ return false;
+ }
+}
+
+bool ValidationTestInputParser::ParseSignedInteger(
+ const DataType& type,
+ const std::string& value_string) {
+ long long int value;
+ if (sscanf(value_string.c_str(), "%lli", &value) != 1)
+ return false;
+
+ switch (type.data_size) {
+ case 1:
+ return ConvertAndAppendData<int8_t>(value);
+ case 2:
+ return ConvertAndAppendData<int16_t>(value);
+ case 4:
+ return ConvertAndAppendData<int32_t>(value);
+ case 8:
+ return ConvertAndAppendData<int64_t>(value);
+ default:
+ assert(false);
+ return false;
+ }
+}
+
+bool ValidationTestInputParser::ParseFloat(const DataType& type,
+ const std::string& value_string) {
+ static_assert(sizeof(float) == 4, "sizeof(float) is not 4");
+
+ float value;
+ if (sscanf(value_string.c_str(), "%f", &value) != 1)
+ return false;
+
+ AppendData(value);
+ return true;
+}
+
+bool ValidationTestInputParser::ParseDouble(const DataType& type,
+ const std::string& value_string) {
+ static_assert(sizeof(double) == 8, "sizeof(double) is not 8");
+
+ double value;
+ if (sscanf(value_string.c_str(), "%lf", &value) != 1)
+ return false;
+
+ AppendData(value);
+ return true;
+}
+
+bool ValidationTestInputParser::ParseBinarySequence(
+ const DataType& type,
+ const std::string& value_string) {
+ if (value_string.size() != 8)
+ return false;
+
+ uint8_t value = 0;
+ for (std::string::const_iterator iter = value_string.begin();
+ iter != value_string.end();
+ ++iter) {
+ value <<= 1;
+ if (*iter == '1')
+ value++;
+ else if (*iter != '0')
+ return false;
+ }
+ AppendData(value);
+ return true;
+}
+
+bool ValidationTestInputParser::ParseDistance(const DataType& type,
+ const std::string& value_string) {
+ if (pending_distance_items_.find(value_string) !=
+ pending_distance_items_.end())
+ return false;
+
+ PendingDistanceItem item = {data_->size(), type.data_size};
+ data_->resize(data_->size() + type.data_size);
+ pending_distance_items_[value_string] = item;
+
+ return true;
+}
+
+bool ValidationTestInputParser::ParseAnchor(const DataType& type,
+ const std::string& value_string) {
+ if (anchors_.find(value_string) != anchors_.end())
+ return false;
+ anchors_.insert(value_string);
+
+ std::map<std::string, PendingDistanceItem>::iterator iter =
+ pending_distance_items_.find(value_string);
+ if (iter == pending_distance_items_.end())
+ return false;
+
+ PendingDistanceItem dist_item = iter->second;
+ pending_distance_items_.erase(iter);
+
+ size_t distance = data_->size() - dist_item.pos;
+ switch (dist_item.data_size) {
+ case 4:
+ return ConvertAndFillData<uint32_t>(dist_item.pos, distance);
+ case 8:
+ return ConvertAndFillData<uint64_t>(dist_item.pos, distance);
+ default:
+ assert(false);
+ return false;
+ }
+}
+
+bool ValidationTestInputParser::ParseHandles(const DataType& type,
+ const std::string& value_string) {
+ // It should be the first item.
+ if (!data_->empty())
+ return false;
+
+ unsigned long long int value;
+ if (!ConvertToUnsignedInteger(value_string, &value))
+ return false;
+
+ if (value > std::numeric_limits<size_t>::max())
+ return false;
+
+ *num_handles_ = static_cast<size_t>(value);
+ return true;
+}
+
+bool ValidationTestInputParser::StartsWith(const Range& range,
+ const char* prefix,
+ size_t prefix_length) {
+ if (static_cast<size_t>(range.second - range.first) < prefix_length)
+ return false;
+
+ return memcmp(range.first, prefix, prefix_length) == 0;
+}
+
+bool ValidationTestInputParser::ConvertToUnsignedInteger(
+ const std::string& value_string,
+ unsigned long long int* value) {
+ const char* format = nullptr;
+ if (value_string.find_first_of("xX") != std::string::npos)
+ format = "%llx";
+ else
+ format = "%llu";
+ return sscanf(value_string.c_str(), format, value) == 1;
+}
+
+} // namespace
+
+bool ParseValidationTestInput(const std::string& input,
+ std::vector<uint8_t>* data,
+ size_t* num_handles,
+ std::string* error_message) {
+ ValidationTestInputParser parser(input, data, num_handles, error_message);
+ return parser.Run();
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.h b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h
new file mode 100644
index 0000000..d5f8c81
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.h
@@ -0,0 +1,120 @@
+// Copyright 2014 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_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+namespace mojo {
+namespace test {
+
+// Input Format of Mojo Message Validation Tests.
+//
+// Data items are separated by whitespaces:
+// - ' ' (0x20) space;
+// - '\t' (0x09) horizontal tab;
+// - '\n' (0x0a) newline;
+// - '\r' (0x0d) carriage return.
+// A comment starts with //, extending to the end of the line.
+// Each data item is of the format [<type>]<value>. The types defined and the
+// corresponding value formats are described below.
+//
+// Type: u1 / u2 / u4 / u8
+// Description: Little-endian 1/2/4/8-byte unsigned integer.
+// Value Format:
+// - Decimal integer: 0|[1-9][0-9]*
+// - Hexadecimal integer: 0[xX][0-9a-fA-F]+
+// - The type prefix (including the square brackets) of 1-byte unsigned
+// integer is optional.
+//
+// Type: s1 / s2 / s4 / s8
+// Description: Little-endian 1/2/4/8-byte signed integer.
+// Value Format:
+// - Decimal integer: [-+]?(0|[1-9][0-9]*)
+// - Hexadecimal integer: [-+]?0[xX][0-9a-fA-F]+
+//
+// Type: b
+// Description: Binary sequence of 1 byte.
+// Value Format: [01]{8}
+//
+// Type: f / d
+// Description: Little-endian IEEE-754 format of float (4 bytes) and double (8
+// bytes).
+// Value Format: [-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
+//
+// Type: dist4 / dist8
+// Description: Little-endian 4/8-byte unsigned integer. The actual value is set
+// to the byte distance from the location of this integer to the location of the
+// anchr item with the same ID. A dist8 and anchr pair can be used to easily
+// represent an encoded pointer. A dist4 and anchr pair can be used to easily
+// calculate struct/array size.
+// Value Format: The value is an ID: [0-9a-zA-Z_]+
+//
+// Type: anchr
+// Description: Mark an anchor location. It doesn’t translate into any actual
+// data.
+// Value Format: The value is an ID of the same format as that of dist4/8.
+//
+// Type: handles
+// Description: The number of handles that are associated with the message. This
+// special item is not part of the message data. If specified, it should be the
+// first item.
+// Value Format: The same format as u1/2/4/8.
+//
+// EXAMPLE:
+//
+// Suppose you have the following Mojo types defined:
+// struct Bar {
+// int32 a;
+// bool b;
+// bool c;
+// };
+// struct Foo {
+// Bar x;
+// uint32 y;
+// };
+//
+// The following describes a valid message whose payload is a Foo struct:
+// // message header
+// [dist4]message_header // num_bytes
+// [u4]3 // version
+// [u4]0 // type
+// [u4]1 // flags
+// [u8]1234 // request_id
+// [anchr]message_header
+//
+// // payload
+// [dist4]foo // num_bytes
+// [u4]2 // version
+// [dist8]bar_ptr // x
+// [u4]0xABCD // y
+// [u4]0 // padding
+// [anchr]foo
+//
+// [anchr]bar_ptr
+// [dist4]bar // num_bytes
+// [u4]3 // version
+// [s4]-1 // a
+// [b]00000010 // b and c
+// 0 0 0 // padding
+// [anchr]bar
+
+// Parses validation test input.
+// On success, |data| and |num_handles| store the parsing result,
+// |error_message| is cleared; on failure, |error_message| is set to a message
+// describing the error, |data| is cleared and |num_handles| set to 0.
+// Note: For now, this method only works on little-endian platforms.
+bool ParseValidationTestInput(const std::string& input,
+ std::vector<uint8_t>* data,
+ size_t* num_handles,
+ std::string* error_message);
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_VALIDATION_TEST_INPUT_PARSER_H_
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
new file mode 100644
index 0000000..fc536c2
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -0,0 +1,480 @@
+// Copyright 2014 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 <stdio.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+template <typename T>
+void Append(std::vector<uint8_t>* data_vector, T data) {
+ size_t pos = data_vector->size();
+ data_vector->resize(pos + sizeof(T));
+ memcpy(&(*data_vector)[pos], &data, sizeof(T));
+}
+
+bool TestInputParser(const std::string& input,
+ bool expected_result,
+ const std::vector<uint8_t>& expected_data,
+ size_t expected_num_handles) {
+ std::vector<uint8_t> data;
+ size_t num_handles;
+ std::string error_message;
+
+ bool result =
+ ParseValidationTestInput(input, &data, &num_handles, &error_message);
+ if (expected_result) {
+ if (result && error_message.empty() && expected_data == data &&
+ expected_num_handles == num_handles) {
+ return true;
+ }
+
+ // Compare with an empty string instead of checking |error_message.empty()|,
+ // so that the message will be printed out if the two are not equal.
+ EXPECT_EQ(std::string(), error_message);
+ EXPECT_EQ(expected_data, data);
+ EXPECT_EQ(expected_num_handles, num_handles);
+ return false;
+ }
+
+ EXPECT_FALSE(error_message.empty());
+ return !result && !error_message.empty();
+}
+
+std::vector<std::string> GetMatchingTests(const std::vector<std::string>& names,
+ const std::string& prefix) {
+ const std::string suffix = ".data";
+ std::vector<std::string> tests;
+ for (size_t i = 0; i < names.size(); ++i) {
+ if (names[i].size() >= suffix.size() &&
+ names[i].substr(0, prefix.size()) == prefix &&
+ names[i].substr(names[i].size() - suffix.size()) == suffix)
+ tests.push_back(names[i].substr(0, names[i].size() - suffix.size()));
+ }
+ return tests;
+}
+
+bool ReadFile(const std::string& path, std::string* result) {
+ FILE* fp = OpenSourceRootRelativeFile(path.c_str());
+ if (!fp) {
+ ADD_FAILURE() << "File not found: " << path;
+ return false;
+ }
+ fseek(fp, 0, SEEK_END);
+ size_t size = static_cast<size_t>(ftell(fp));
+ if (size == 0) {
+ result->clear();
+ fclose(fp);
+ return true;
+ }
+ fseek(fp, 0, SEEK_SET);
+ result->resize(size);
+ size_t size_read = fread(&result->at(0), 1, size, fp);
+ fclose(fp);
+ return size == size_read;
+}
+
+bool ReadAndParseDataFile(const std::string& path,
+ std::vector<uint8_t>* data,
+ size_t* num_handles) {
+ std::string input;
+ if (!ReadFile(path, &input))
+ return false;
+
+ std::string error_message;
+ if (!ParseValidationTestInput(input, data, num_handles, &error_message)) {
+ ADD_FAILURE() << error_message;
+ return false;
+ }
+
+ return true;
+}
+
+bool ReadResultFile(const std::string& path, std::string* result) {
+ if (!ReadFile(path, result))
+ return false;
+
+ // Result files are new-line delimited text files. Remove any CRs.
+ result->erase(std::remove(result->begin(), result->end(), '\r'),
+ result->end());
+
+ // Remove trailing LFs.
+ size_t pos = result->find_last_not_of('\n');
+ if (pos == std::string::npos)
+ result->clear();
+ else
+ result->resize(pos + 1);
+
+ return true;
+}
+
+std::string GetPath(const std::string& root, const std::string& suffix) {
+ return "mojo/public/interfaces/bindings/tests/data/validation/" + root +
+ suffix;
+}
+
+// |message| should be a newly created object.
+bool ReadTestCase(const std::string& test,
+ Message* message,
+ std::string* expected) {
+ std::vector<uint8_t> data;
+ size_t num_handles;
+ if (!ReadAndParseDataFile(GetPath(test, ".data"), &data, &num_handles) ||
+ !ReadResultFile(GetPath(test, ".expected"), expected)) {
+ return false;
+ }
+
+ message->AllocUninitializedData(static_cast<uint32_t>(data.size()));
+ if (!data.empty())
+ memcpy(message->mutable_data(), &data[0], data.size());
+ message->mutable_handles()->resize(num_handles);
+
+ return true;
+}
+
+void RunValidationTests(const std::string& prefix,
+ MessageReceiver* test_message_receiver) {
+ std::vector<std::string> names =
+ EnumerateSourceRootRelativeDirectory(GetPath("", ""));
+ std::vector<std::string> tests = GetMatchingTests(names, prefix);
+
+ for (size_t i = 0; i < tests.size(); ++i) {
+ Message message;
+ std::string expected;
+ ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected));
+
+ std::string result;
+ mojo::internal::ValidationErrorObserverForTesting observer;
+ mojo_ignore_result(test_message_receiver->Accept(&message));
+ if (observer.last_error() == mojo::internal::VALIDATION_ERROR_NONE)
+ result = "PASS";
+ else
+ result = mojo::internal::ValidationErrorToString(observer.last_error());
+
+ EXPECT_EQ(expected, result) << "failed test: " << tests[i];
+ }
+}
+
+class DummyMessageReceiver : public MessageReceiver {
+ public:
+ bool Accept(Message* message) override {
+ return true; // Any message is OK.
+ }
+};
+
+using ValidationTest = testing::Test;
+
+class ValidationIntegrationTest : public ValidationTest {
+ public:
+ ValidationIntegrationTest()
+ : loop_(common::MessagePumpMojo::Create()),
+ test_message_receiver_(nullptr) {}
+
+ ~ValidationIntegrationTest() override {}
+
+ void SetUp() override {
+ ScopedMessagePipeHandle tester_endpoint;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ CreateMessagePipe(nullptr, &tester_endpoint, &testee_endpoint_));
+ test_message_receiver_ =
+ new TestMessageReceiver(this, tester_endpoint.Pass());
+ }
+
+ void TearDown() override {
+ delete test_message_receiver_;
+ test_message_receiver_ = nullptr;
+
+ // Make sure that the other end receives the OnConnectionError()
+ // notification.
+ PumpMessages();
+ }
+
+ MessageReceiver* test_message_receiver() { return test_message_receiver_; }
+
+ ScopedMessagePipeHandle testee_endpoint() { return testee_endpoint_.Pass(); }
+
+ private:
+ class TestMessageReceiver : public MessageReceiver {
+ public:
+ TestMessageReceiver(ValidationIntegrationTest* owner,
+ ScopedMessagePipeHandle handle)
+ : owner_(owner), connector_(handle.Pass()) {
+ connector_.set_enforce_errors_from_incoming_receiver(false);
+ }
+ ~TestMessageReceiver() override {}
+
+ bool Accept(Message* message) override {
+ bool rv = connector_.Accept(message);
+ owner_->PumpMessages();
+ return rv;
+ }
+
+ public:
+ ValidationIntegrationTest* owner_;
+ mojo::internal::Connector connector_;
+ };
+
+ void PumpMessages() { loop_.RunUntilIdle(); }
+
+ base::MessageLoop loop_;
+ TestMessageReceiver* test_message_receiver_;
+ ScopedMessagePipeHandle testee_endpoint_;
+};
+
+class IntegrationTestInterfaceImpl : public IntegrationTestInterface {
+ public:
+ ~IntegrationTestInterfaceImpl() override {}
+
+ void Method0(BasicStructPtr param0,
+ const Method0Callback& callback) override {
+ callback.Run(Array<uint8_t>::New(0u));
+ }
+};
+
+TEST_F(ValidationTest, InputParser) {
+ {
+ // The parser, as well as Append() defined above, assumes that this code is
+ // running on a little-endian platform. Test whether that is true.
+ uint16_t x = 1;
+ ASSERT_EQ(1, *(reinterpret_cast<char*>(&x)));
+ }
+ {
+ // Test empty input.
+ std::string input;
+ std::vector<uint8_t> expected;
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ // Test input that only consists of comments and whitespaces.
+ std::string input = " \t // hello world \n\r \t// the answer is 42 ";
+ std::vector<uint8_t> expected;
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ std::string input =
+ "[u1]0x10// hello world !! \n\r \t [u2]65535 \n"
+ "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff";
+ std::vector<uint8_t> expected;
+ Append(&expected, static_cast<uint8_t>(0x10));
+ Append(&expected, static_cast<uint16_t>(65535));
+ Append(&expected, static_cast<uint32_t>(65536));
+ Append(&expected, static_cast<uint64_t>(0xffffffffffffffff));
+ Append(&expected, static_cast<uint8_t>(0));
+ Append(&expected, static_cast<uint8_t>(0xff));
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ std::string input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40";
+ std::vector<uint8_t> expected;
+ Append(&expected, -static_cast<int64_t>(0x800));
+ Append(&expected, static_cast<int8_t>(-128));
+ Append(&expected, static_cast<int16_t>(0));
+ Append(&expected, static_cast<int32_t>(-40));
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ std::string input = "[b]00001011 [b]10000000 // hello world\r [b]00000000";
+ std::vector<uint8_t> expected;
+ Append(&expected, static_cast<uint8_t>(11));
+ Append(&expected, static_cast<uint8_t>(128));
+ Append(&expected, static_cast<uint8_t>(0));
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ std::string input = "[f]+.3e9 [d]-10.03";
+ std::vector<uint8_t> expected;
+ Append(&expected, +.3e9f);
+ Append(&expected, -10.03);
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ std::string input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar";
+ std::vector<uint8_t> expected;
+ Append(&expected, static_cast<uint32_t>(14));
+ Append(&expected, static_cast<uint8_t>(0));
+ Append(&expected, static_cast<uint64_t>(9));
+ Append(&expected, static_cast<uint8_t>(0));
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 0));
+ }
+ {
+ std::string input = "// This message has handles! \n[handles]50 [u8]2";
+ std::vector<uint8_t> expected;
+ Append(&expected, static_cast<uint64_t>(2));
+
+ EXPECT_TRUE(TestInputParser(input, true, expected, 50));
+ }
+
+ // Test some failure cases.
+ {
+ const char* error_inputs[] = {"/ hello world",
+ "[u1]x",
+ "[u2]-1000",
+ "[u1]0x100",
+ "[s2]-0x8001",
+ "[b]1",
+ "[b]1111111k",
+ "[dist4]unmatched",
+ "[anchr]hello [dist8]hello",
+ "[dist4]a [dist4]a [anchr]a",
+ "[dist4]a [anchr]a [dist4]a [anchr]a",
+ "0 [handles]50",
+ nullptr};
+
+ for (size_t i = 0; error_inputs[i]; ++i) {
+ std::vector<uint8_t> expected;
+ if (!TestInputParser(error_inputs[i], false, expected, 0))
+ ADD_FAILURE() << "Unexpected test result for: " << error_inputs[i];
+ }
+ }
+}
+
+TEST_F(ValidationTest, Conformance) {
+ DummyMessageReceiver dummy_receiver;
+ mojo::internal::FilterChain validators(&dummy_receiver);
+ validators.Append<mojo::internal::MessageHeaderValidator>();
+ validators.Append<ConformanceTestInterface::RequestValidator_>();
+
+ RunValidationTests("conformance_", validators.GetHead());
+}
+
+// This test is similar to Conformance test but its goal is specifically
+// do bounds-check testing of message validation. For example we test the
+// detection of off-by-one errors in method ordinals.
+TEST_F(ValidationTest, BoundsCheck) {
+ DummyMessageReceiver dummy_receiver;
+ mojo::internal::FilterChain validators(&dummy_receiver);
+ validators.Append<mojo::internal::MessageHeaderValidator>();
+ validators.Append<BoundsCheckTestInterface::RequestValidator_>();
+
+ RunValidationTests("boundscheck_", validators.GetHead());
+}
+
+// This test is similar to the Conformance test but for responses.
+TEST_F(ValidationTest, ResponseConformance) {
+ DummyMessageReceiver dummy_receiver;
+ mojo::internal::FilterChain validators(&dummy_receiver);
+ validators.Append<mojo::internal::MessageHeaderValidator>();
+ validators.Append<ConformanceTestInterface::ResponseValidator_>();
+
+ RunValidationTests("resp_conformance_", validators.GetHead());
+}
+
+// This test is similar to the BoundsCheck test but for responses.
+TEST_F(ValidationTest, ResponseBoundsCheck) {
+ DummyMessageReceiver dummy_receiver;
+ mojo::internal::FilterChain validators(&dummy_receiver);
+ validators.Append<mojo::internal::MessageHeaderValidator>();
+ validators.Append<BoundsCheckTestInterface::ResponseValidator_>();
+
+ RunValidationTests("resp_boundscheck_", validators.GetHead());
+}
+
+// Test that InterfacePtr<X> applies the correct validators and they don't
+// conflict with each other:
+// - MessageHeaderValidator
+// - X::ResponseValidator_
+TEST_F(ValidationIntegrationTest, InterfacePtr) {
+ IntegrationTestInterfacePtr interface_ptr = MakeProxy(
+ InterfacePtrInfo<IntegrationTestInterface>(testee_endpoint().Pass(), 0u));
+ interface_ptr.internal_state()->router_for_testing()->EnableTestingMode();
+
+ RunValidationTests("integration_intf_resp", test_message_receiver());
+ RunValidationTests("integration_msghdr", test_message_receiver());
+}
+
+// Test that Binding<X> applies the correct validators and they don't
+// conflict with each other:
+// - MessageHeaderValidator
+// - X::RequestValidator_
+TEST_F(ValidationIntegrationTest, Binding) {
+ IntegrationTestInterfaceImpl interface_impl;
+ Binding<IntegrationTestInterface> binding(
+ &interface_impl,
+ MakeRequest<IntegrationTestInterface>(testee_endpoint().Pass()));
+ binding.internal_router()->EnableTestingMode();
+
+ RunValidationTests("integration_intf_rqst", test_message_receiver());
+ RunValidationTests("integration_msghdr", test_message_receiver());
+}
+
+// Test pointer validation (specifically, that the encoded offset is 32-bit)
+TEST_F(ValidationTest, ValidateEncodedPointer) {
+ uint64_t offset;
+
+ offset = 0ULL;
+ EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
+
+ offset = 1ULL;
+ EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
+
+ // offset must be <= 32-bit.
+ offset = std::numeric_limits<uint32_t>::max() + 1ULL;
+ EXPECT_FALSE(mojo::internal::ValidateEncodedPointer(&offset));
+}
+
+// Tests the IsValidValue() function generated for BasicEnum.
+TEST(EnumValueValidationTest, BasicEnum) {
+ // BasicEnum can have -3,0,1,10 as possible integral values.
+ EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-4)));
+ EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-3)));
+ EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-2)));
+ EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(-1)));
+ EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(0)));
+ EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(1)));
+ EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(2)));
+ EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(9)));
+ // In the mojom, we represent this value as hex (0xa).
+ EXPECT_TRUE(BasicEnum_IsValidValue(static_cast<BasicEnum>(10)));
+ EXPECT_FALSE(BasicEnum_IsValidValue(static_cast<BasicEnum>(11)));
+}
+
+// Tests the IsValidValue() method generated for StructWithEnum.
+TEST(EnumValueValidationTest, EnumWithin) {
+ // StructWithEnum::EnumWithin can have [0,4] as possible integral values.
+ EXPECT_FALSE(StructWithEnum::EnumWithin_IsValidValue(
+ static_cast<StructWithEnum::EnumWithin>(-1)));
+ EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue(
+ static_cast<StructWithEnum::EnumWithin>(0)));
+ EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue(
+ static_cast<StructWithEnum::EnumWithin>(1)));
+ EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue(
+ static_cast<StructWithEnum::EnumWithin>(2)));
+ EXPECT_TRUE(StructWithEnum::EnumWithin_IsValidValue(
+ static_cast<StructWithEnum::EnumWithin>(3)));
+ EXPECT_FALSE(StructWithEnum::EnumWithin_IsValidValue(
+ static_cast<StructWithEnum::EnumWithin>(4)));
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/versioning_apptest.cc b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
new file mode 100644
index 0000000..a1cb37f
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
@@ -0,0 +1,121 @@
+// Copyright 2015 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/application/public/cpp/application_impl.h"
+#include "mojo/application/public/cpp/application_test_base.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/bindings/tests/versioning_test_client.mojom.h"
+
+namespace mojo {
+namespace test {
+namespace versioning {
+
+class VersioningApplicationTest : public ApplicationTestBase {
+ public:
+ VersioningApplicationTest() : ApplicationTestBase() {}
+ ~VersioningApplicationTest() override {}
+
+ protected:
+ // ApplicationTestBase overrides.
+ void SetUp() override {
+ ApplicationTestBase::SetUp();
+
+ application_impl()->ConnectToService("mojo:versioning_test_service",
+ &database_);
+ }
+
+ HumanResourceDatabasePtr database_;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(VersioningApplicationTest);
+};
+
+TEST_F(VersioningApplicationTest, Struct) {
+ // The service side uses a newer version of Employee defintion.
+ // The returned struct should be truncated.
+ EmployeePtr employee(Employee::New());
+ employee->employee_id = 1;
+ employee->name = "Homer Simpson";
+ employee->department = DEPARTMENT_DEV;
+
+ database_->QueryEmployee(1, true,
+ [&employee](EmployeePtr returned_employee,
+ Array<uint8_t> returned_finger_print) {
+ EXPECT_TRUE(employee->Equals(*returned_employee));
+ EXPECT_FALSE(returned_finger_print.is_null());
+ });
+ database_.WaitForIncomingResponse();
+
+ // Passing a struct of older version to the service side works.
+ EmployeePtr new_employee(Employee::New());
+ new_employee->employee_id = 2;
+ new_employee->name = "Marge Simpson";
+ new_employee->department = DEPARTMENT_SALES;
+
+ database_->AddEmployee(new_employee.Clone(),
+ [](bool success) { EXPECT_TRUE(success); });
+ database_.WaitForIncomingResponse();
+
+ database_->QueryEmployee(
+ 2, false, [&new_employee](EmployeePtr returned_employee,
+ Array<uint8_t> returned_finger_print) {
+ EXPECT_TRUE(new_employee->Equals(*returned_employee));
+ EXPECT_TRUE(returned_finger_print.is_null());
+ });
+ database_.WaitForIncomingResponse();
+}
+
+TEST_F(VersioningApplicationTest, QueryVersion) {
+ EXPECT_EQ(0u, database_.version());
+ database_.QueryVersion([](uint32_t version) { EXPECT_EQ(1u, version); });
+ database_.WaitForIncomingResponse();
+ EXPECT_EQ(1u, database_.version());
+}
+
+TEST_F(VersioningApplicationTest, RequireVersion) {
+ EXPECT_EQ(0u, database_.version());
+
+ database_.RequireVersion(1);
+ EXPECT_EQ(1u, database_.version());
+ database_->QueryEmployee(3, false,
+ [](EmployeePtr returned_employee,
+ Array<uint8_t> returned_finger_print) {});
+ database_.WaitForIncomingResponse();
+ EXPECT_FALSE(database_.encountered_error());
+
+ // Requiring a version higher than what the service side implements will close
+ // the pipe.
+ database_.RequireVersion(3);
+ EXPECT_EQ(3u, database_.version());
+ database_->QueryEmployee(1, false,
+ [](EmployeePtr returned_employee,
+ Array<uint8_t> returned_finger_print) {});
+ database_.WaitForIncomingResponse();
+ EXPECT_TRUE(database_.encountered_error());
+}
+
+TEST_F(VersioningApplicationTest, CallNonexistentMethod) {
+ EXPECT_EQ(0u, database_.version());
+
+ Array<uint8_t> new_finger_print(128);
+ for (size_t i = 0; i < 128; ++i)
+ new_finger_print[i] = i + 13;
+
+ // Although the client side doesn't know whether the service side supports
+ // version 1, calling a version 1 method succeeds as long as the service side
+ // supports version 1.
+ database_->AttachFingerPrint(1, new_finger_print.Clone(),
+ [](bool success) { EXPECT_TRUE(success); });
+ database_.WaitForIncomingResponse();
+
+ // Calling a version 2 method (which the service side doesn't support) closes
+ // the pipe.
+ database_->ListEmployeeIds([](Array<uint64_t> ids) { EXPECT_TRUE(false); });
+ database_.WaitForIncomingResponse();
+ EXPECT_TRUE(database_.encountered_error());
+}
+
+} // namespace versioning
+} // namespace examples
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/versioning_test_service.cc b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
new file mode 100644
index 0000000..cc52595
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
@@ -0,0 +1,125 @@
+// Copyright 2015 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 <map>
+
+#include "mojo/application/public/cpp/application_connection.h"
+#include "mojo/application/public/cpp/application_delegate.h"
+#include "mojo/application/public/cpp/application_runner.h"
+#include "mojo/application/public/cpp/interface_factory.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/bindings/tests/versioning_test_service.mojom.h"
+
+namespace mojo {
+namespace test {
+namespace versioning {
+
+struct EmployeeInfo {
+ public:
+ EmployeeInfo() {}
+
+ EmployeePtr employee;
+ Array<uint8_t> finger_print;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(EmployeeInfo);
+};
+
+class HumanResourceDatabaseImpl : public HumanResourceDatabase {
+ public:
+ explicit HumanResourceDatabaseImpl(
+ InterfaceRequest<HumanResourceDatabase> request)
+ : strong_binding_(this, request.Pass()) {
+ // Pretend that there is already some data in the system.
+ EmployeeInfo* info = new EmployeeInfo();
+ employees_[1] = info;
+ info->employee = Employee::New();
+ info->employee->employee_id = 1;
+ info->employee->name = "Homer Simpson";
+ info->employee->department = DEPARTMENT_DEV;
+ info->employee->birthday = Date::New();
+ info->employee->birthday->year = 1955;
+ info->employee->birthday->month = 5;
+ info->employee->birthday->day = 12;
+ info->finger_print.resize(1024);
+ for (uint32_t i = 0; i < 1024; ++i)
+ info->finger_print[i] = i;
+ }
+
+ ~HumanResourceDatabaseImpl() override {
+ for (auto iter = employees_.begin(); iter != employees_.end(); ++iter)
+ delete iter->second;
+ }
+
+ void AddEmployee(EmployeePtr employee,
+ const AddEmployeeCallback& callback) override {
+ uint64_t id = employee->employee_id;
+ if (employees_.find(id) == employees_.end())
+ employees_[id] = new EmployeeInfo();
+ employees_[id]->employee = employee.Pass();
+ callback.Run(true);
+ }
+
+ void QueryEmployee(uint64_t id,
+ bool retrieve_finger_print,
+ const QueryEmployeeCallback& callback) override {
+ if (employees_.find(id) == employees_.end()) {
+ callback.Run(nullptr, Array<uint8_t>());
+ return;
+ }
+ callback.Run(employees_[id]->employee.Clone(),
+ retrieve_finger_print ? employees_[id]->finger_print.Clone()
+ : Array<uint8_t>());
+ }
+
+ void AttachFingerPrint(uint64_t id,
+ Array<uint8_t> finger_print,
+ const AttachFingerPrintCallback& callback) override {
+ if (employees_.find(id) == employees_.end()) {
+ callback.Run(false);
+ return;
+ }
+ employees_[id]->finger_print = finger_print.Pass();
+ callback.Run(true);
+ }
+
+ private:
+ std::map<uint64_t, EmployeeInfo*> employees_;
+
+ StrongBinding<HumanResourceDatabase> strong_binding_;
+};
+
+class HumanResourceSystemServer
+ : public ApplicationDelegate,
+ public InterfaceFactory<HumanResourceDatabase> {
+ public:
+ HumanResourceSystemServer() {}
+
+ // ApplicationDelegate implementation.
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ connection->AddService<HumanResourceDatabase>(this);
+ return true;
+ }
+
+ // InterfaceFactory<HumanResourceDatabase> implementation.
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<HumanResourceDatabase> request) override {
+ // It will be deleted automatically when the underlying pipe encounters a
+ // connection error.
+ new HumanResourceDatabaseImpl(request.Pass());
+ }
+};
+
+} // namespace versioning
+} // namespace test
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle application_request) {
+ mojo::ApplicationRunner runner(
+ new mojo::test::versioning::HumanResourceSystemServer());
+
+ return runner.Run(application_request);
+}
diff --git a/mojo/public/cpp/bindings/type_converter.h b/mojo/public/cpp/bindings/type_converter.h
new file mode 100644
index 0000000..ff94cda
--- /dev/null
+++ b/mojo/public/cpp/bindings/type_converter.h
@@ -0,0 +1,92 @@
+// Copyright 2014 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_TYPE_CONVERTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+
+namespace mojo {
+
+// Specialize the following class:
+// template <typename T, typename U> struct TypeConverter;
+// to perform type conversion for Mojom-defined structs and arrays. Here, T is
+// the target type; U is the input type.
+//
+// Specializations should implement the following interfaces:
+// namespace mojo {
+// template <>
+// struct TypeConverter<X, Y> {
+// static X Convert(const Y& input);
+// };
+// template <>
+// struct TypeConverter<Y, X> {
+// static Y Convert(const X& input);
+// };
+// }
+//
+// EXAMPLE:
+//
+// Suppose you have the following Mojom-defined struct:
+//
+// module geometry {
+// struct Point {
+// int32 x;
+// int32 y;
+// };
+// }
+//
+// Now, imagine you wanted to write a TypeConverter specialization for
+// gfx::Point. It might look like this:
+//
+// namespace mojo {
+// template <>
+// struct TypeConverter<geometry::PointPtr, gfx::Point> {
+// static geometry::PointPtr Convert(const gfx::Point& input) {
+// geometry::PointPtr result;
+// result->x = input.x();
+// result->y = input.y();
+// return result.Pass();
+// }
+// };
+// template <>
+// struct TypeConverter<gfx::Point, geometry::PointPtr> {
+// static gfx::Point Convert(const geometry::PointPtr& input) {
+// return input ? gfx::Point(input->x, input->y) : gfx::Point();
+// }
+// };
+// }
+//
+// With the above TypeConverter defined, it is possible to write code like this:
+//
+// void AcceptPoint(const geometry::PointPtr& input) {
+// // With an explicit cast using the .To<> method.
+// gfx::Point pt = input.To<gfx::Point>();
+//
+// // With an explicit cast using the static From() method.
+// geometry::PointPtr output = geometry::Point::From(pt);
+//
+// // Inferring the input type using the ConvertTo helper function.
+// gfx::Point pt2 = ConvertTo<gfx::Point>(input);
+// }
+//
+template <typename T, typename U>
+struct TypeConverter;
+
+// The following specialization is useful when you are converting between
+// Array<POD> and std::vector<POD>.
+template <typename T>
+struct TypeConverter<T, T> {
+ static T Convert(const T& obj) { return obj; }
+};
+
+// The following helper function is useful for shorthand. The compiler can infer
+// the input type, so you can write:
+// OutputType out = ConvertTo<OutputType>(input);
+template <typename T, typename U>
+inline T ConvertTo(const U& obj) {
+ return TypeConverter<T, U>::Convert(obj);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
diff --git a/mojo/public/cpp/environment/BUILD.gn b/mojo/public/cpp/environment/BUILD.gn
new file mode 100644
index 0000000..fe3a011
--- /dev/null
+++ b/mojo/public/cpp/environment/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2014 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.
+
+import("../../mojo_sdk.gni")
+
+mojo_sdk_source_set("environment") {
+ sources = [
+ "async_waiter.h",
+ "environment.h",
+ "logging.h",
+ "task_tracker.h",
+ ]
+
+ mojo_sdk_public_deps = [ "mojo/public/c/environment" ]
+
+ mojo_sdk_deps = [
+ "mojo/public/cpp/bindings:callback",
+ "mojo/public/cpp/system",
+ ]
+}
+
+mojo_sdk_source_set("standalone") {
+ sources = [
+ "lib/async_waiter.cc",
+ "lib/default_async_waiter.cc",
+ "lib/default_async_waiter.h",
+ "lib/default_logger.cc",
+ "lib/default_logger.h",
+ "lib/default_task_tracker.cc",
+ "lib/default_task_tracker.h",
+ "lib/environment.cc",
+ "lib/logging.cc",
+ "lib/scoped_task_tracking.cc",
+ "lib/scoped_task_tracking.h",
+ ]
+
+ public_deps = [
+ ":environment",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/c/environment",
+ "mojo/public/cpp/system",
+ "mojo/public/cpp/utility",
+ ]
+}
diff --git a/mojo/public/cpp/environment/async_waiter.h b/mojo/public/cpp/environment/async_waiter.h
new file mode 100644
index 0000000..83b7c8a
--- /dev/null
+++ b/mojo/public/cpp/environment/async_waiter.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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_ENVIRONMENT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/handle.h"
+
+namespace mojo {
+
+// A class that waits until a handle is ready and calls |callback| with the
+// result. If the AsyncWaiter is deleted before the handle is ready, the wait is
+// cancelled and the callback will not be called.
+class AsyncWaiter {
+ public:
+ typedef mojo::Callback<void(MojoResult)> Callback;
+
+ AsyncWaiter(Handle handle,
+ MojoHandleSignals signals,
+ const Callback& callback);
+ ~AsyncWaiter();
+
+ private:
+ static void WaitComplete(void* waiter, MojoResult result);
+ void WaitCompleteInternal(MojoResult result);
+
+ const MojoAsyncWaiter* waiter_;
+ MojoAsyncWaitID id_;
+ const Callback callback_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiter);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_ASYNC_WAITER_H_
diff --git a/mojo/public/cpp/environment/environment.h b/mojo/public/cpp/environment/environment.h
new file mode 100644
index 0000000..3a54428
--- /dev/null
+++ b/mojo/public/cpp/environment/environment.h
@@ -0,0 +1,53 @@
+// Copyright 2014 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_ENVIRONMENT_ENVIRONMENT_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+struct MojoAsyncWaiter;
+struct MojoLogger;
+
+namespace mojo {
+
+struct TaskTracker;
+
+// Other parts of the Mojo C++ APIs use the *static* methods of this class.
+//
+// The "standalone" implementation of this class requires that this class (in
+// the lib/ subdirectory) be instantiated (and remain so) while using the Mojo
+// C++ APIs. I.e., the static methods depend on things set up by the constructor
+// and torn down by the destructor.
+//
+// Other implementations may not have this requirement.
+class Environment {
+ public:
+ Environment();
+ // This constructor allows the standard implementations to be overridden (set
+ // a parameter to null to get the standard implementation).
+ Environment(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger,
+ const TaskTracker* default_task_tracker);
+ ~Environment();
+
+ static const MojoAsyncWaiter* GetDefaultAsyncWaiter();
+ static const MojoLogger* GetDefaultLogger();
+ static const TaskTracker* GetDefaultTaskTracker();
+
+ // These instantiate and destroy an environment-specific run loop for the
+ // current thread, allowing |GetDefaultAsyncWaiter()| to be used. (The run
+ // loop itself should be accessible via thread-local storage, using methods
+ // specific to the run loop implementation.) Creating and destroying nested
+ // run loops is not supported.
+ static void InstantiateDefaultRunLoop();
+ static void DestroyDefaultRunLoop();
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Environment);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
diff --git a/mojo/public/cpp/environment/lib/async_waiter.cc b/mojo/public/cpp/environment/lib/async_waiter.cc
new file mode 100644
index 0000000..599a649
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/async_waiter.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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/environment/async_waiter.h"
+
+namespace mojo {
+
+AsyncWaiter::AsyncWaiter(Handle handle,
+ MojoHandleSignals signals,
+ const Callback& callback)
+ : waiter_(Environment::GetDefaultAsyncWaiter()),
+ id_(0),
+ callback_(callback) {
+ id_ = waiter_->AsyncWait(handle.value(), signals, MOJO_DEADLINE_INDEFINITE,
+ &AsyncWaiter::WaitComplete, this);
+}
+
+AsyncWaiter::~AsyncWaiter() {
+ if (id_)
+ waiter_->CancelWait(id_);
+}
+
+// static
+void AsyncWaiter::WaitComplete(void* waiter, MojoResult result) {
+ static_cast<AsyncWaiter*>(waiter)->WaitCompleteInternal(result);
+}
+
+void AsyncWaiter::WaitCompleteInternal(MojoResult result) {
+ id_ = 0;
+ callback_.Run(result);
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/default_async_waiter.cc b/mojo/public/cpp/environment/lib/default_async_waiter.cc
new file mode 100644
index 0000000..4a588a9
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_async_waiter.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 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/environment/lib/default_async_waiter.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+
+namespace mojo {
+
+namespace {
+
+// RunLoopHandler implementation used for a request to AsyncWait(). There are
+// two ways RunLoopHandlerImpl is deleted:
+// . when the handle is ready (or errored).
+// . when CancelWait() is invoked.
+class RunLoopHandlerImpl : public RunLoopHandler {
+ public:
+ RunLoopHandlerImpl(const Handle& handle,
+ MojoAsyncWaitCallback callback,
+ void* closure)
+ : handle_(handle), callback_(callback), closure_(closure) {}
+
+ ~RunLoopHandlerImpl() override { RunLoop::current()->RemoveHandler(handle_); }
+
+ // RunLoopHandler:
+ void OnHandleReady(const Handle& handle) override {
+ NotifyCallback(MOJO_RESULT_OK);
+ }
+
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ NotifyCallback(result);
+ }
+
+ private:
+ void NotifyCallback(MojoResult result) {
+ // Delete this to unregister the handle. That way if the callback
+ // reregisters everything is ok.
+ MojoAsyncWaitCallback callback = callback_;
+ void* closure = closure_;
+ delete this;
+
+ callback(closure, result);
+ }
+
+ const Handle handle_;
+ MojoAsyncWaitCallback callback_;
+ void* closure_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopHandlerImpl);
+};
+
+MojoAsyncWaitID AsyncWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ MojoAsyncWaitCallback callback,
+ void* closure) {
+ RunLoop* run_loop = RunLoop::current();
+ assert(run_loop);
+
+ // |run_loop_handler| is destroyed either when the handle is ready or if
+ // CancelWait is invoked.
+ RunLoopHandlerImpl* run_loop_handler =
+ new RunLoopHandlerImpl(Handle(handle), callback, closure);
+ run_loop->AddHandler(run_loop_handler, Handle(handle), signals, deadline);
+ return reinterpret_cast<MojoAsyncWaitID>(run_loop_handler);
+}
+
+void CancelWait(MojoAsyncWaitID wait_id) {
+ delete reinterpret_cast<RunLoopHandlerImpl*>(wait_id);
+}
+
+} // namespace
+
+namespace internal {
+
+const MojoAsyncWaiter kDefaultAsyncWaiter = {AsyncWait, CancelWait};
+
+} // namespace internal
+
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/default_async_waiter.h b/mojo/public/cpp/environment/lib/default_async_waiter.h
new file mode 100644
index 0000000..49ce233
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_async_waiter.h
@@ -0,0 +1,18 @@
+// Copyright 2014 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_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
+
+struct MojoAsyncWaiter;
+
+namespace mojo {
+namespace internal {
+
+extern const MojoAsyncWaiter kDefaultAsyncWaiter;
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
diff --git a/mojo/public/cpp/environment/lib/default_logger.cc b/mojo/public/cpp/environment/lib/default_logger.cc
new file mode 100644
index 0000000..ba787d1
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_logger.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 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/environment/lib/default_logger.h"
+
+#include <stdio.h>
+#include <stdlib.h> // For |abort()|.
+
+#include <algorithm>
+
+#include "mojo/public/c/environment/logger.h"
+
+namespace mojo {
+
+namespace {
+
+MojoLogLevel g_minimum_log_level = MOJO_LOG_LEVEL_INFO;
+
+const char* GetLogLevelString(MojoLogLevel log_level) {
+ if (log_level <= MOJO_LOG_LEVEL_VERBOSE - 3)
+ return "VERBOSE4+";
+ switch (log_level) {
+ case MOJO_LOG_LEVEL_VERBOSE - 2:
+ return "VERBOSE3";
+ case MOJO_LOG_LEVEL_VERBOSE - 1:
+ return "VERBOSE2";
+ case MOJO_LOG_LEVEL_VERBOSE:
+ return "VERBOSE1";
+ case MOJO_LOG_LEVEL_INFO:
+ return "INFO";
+ case MOJO_LOG_LEVEL_WARNING:
+ return "WARNING";
+ case MOJO_LOG_LEVEL_ERROR:
+ return "ERROR";
+ }
+ // Consider everything higher to be fatal.
+ return "FATAL";
+}
+
+void LogMessage(MojoLogLevel log_level,
+ const char* source_file,
+ uint32_t source_line,
+ const char* message) {
+ if (log_level < g_minimum_log_level)
+ return;
+
+ // TODO(vtl): Add timestamp also?
+ if (source_file) {
+ fprintf(stderr, "%s: %s(%u): %s\n", GetLogLevelString(log_level),
+ source_file, static_cast<unsigned>(source_line), message);
+ } else {
+ fprintf(stderr, "%s: %s\n", GetLogLevelString(log_level), message);
+ }
+ if (log_level >= MOJO_LOG_LEVEL_FATAL)
+ abort();
+}
+
+MojoLogLevel GetMinimumLogLevel() {
+ return g_minimum_log_level;
+}
+
+void SetMinimumLogLevel(MojoLogLevel minimum_log_level) {
+ g_minimum_log_level = std::min(minimum_log_level, MOJO_LOG_LEVEL_FATAL);
+}
+
+} // namespace
+
+namespace internal {
+
+const MojoLogger kDefaultLogger = {LogMessage,
+ GetMinimumLogLevel,
+ SetMinimumLogLevel};
+
+} // namespace internal
+
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/default_logger.h b/mojo/public/cpp/environment/lib/default_logger.h
new file mode 100644
index 0000000..4db3233
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_logger.h
@@ -0,0 +1,18 @@
+// Copyright 2014 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_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
+
+struct MojoLogger;
+
+namespace mojo {
+namespace internal {
+
+extern const MojoLogger kDefaultLogger;
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
diff --git a/mojo/public/cpp/environment/lib/default_task_tracker.cc b/mojo/public/cpp/environment/lib/default_task_tracker.cc
new file mode 100644
index 0000000..a99f1c7
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_task_tracker.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 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/environment/lib/default_task_tracker.h"
+
+#include "mojo/public/cpp/environment/task_tracker.h"
+
+namespace mojo {
+
+namespace {
+
+//
+// The standalone task tracker does nothing.
+//
+
+TaskTrackingId StartTracking(const char* function_name,
+ const char* file_name,
+ int line_number,
+ const void* program_counter) {
+ return TaskTrackingId(0);
+}
+
+void EndTracking(const TaskTrackingId id) {
+}
+
+void SetEnabled(bool enabled) {
+}
+
+} // namespace
+
+namespace internal {
+
+const TaskTracker kDefaultTaskTracker = {&StartTracking,
+ &EndTracking,
+ &SetEnabled};
+
+} // namespace internal
+
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/default_task_tracker.h b/mojo/public/cpp/environment/lib/default_task_tracker.h
new file mode 100644
index 0000000..7a0c064
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_task_tracker.h
@@ -0,0 +1,19 @@
+// Copyright 2015 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_ENVIRONMENT_LIB_DEFAULT_TASK_TRACKER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_TASK_TRACKER_H_
+
+namespace mojo {
+
+struct TaskTracker;
+
+namespace internal {
+
+extern const TaskTracker kDefaultTaskTracker;
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_TASK_TRACKER_H_
diff --git a/mojo/public/cpp/environment/lib/environment.cc b/mojo/public/cpp/environment/lib/environment.cc
new file mode 100644
index 0000000..8c7c930
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/environment.cc
@@ -0,0 +1,93 @@
+// Copyright 2014 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/environment/environment.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/lib/default_async_waiter.h"
+#include "mojo/public/cpp/environment/lib/default_logger.h"
+#include "mojo/public/cpp/environment/lib/default_task_tracker.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+namespace {
+
+const MojoAsyncWaiter* g_default_async_waiter = nullptr;
+const MojoLogger* g_default_logger = nullptr;
+const TaskTracker* g_default_task_tracker = nullptr;
+
+void Init(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger,
+ const TaskTracker* default_task_tracker) {
+ g_default_async_waiter = default_async_waiter
+ ? default_async_waiter
+ : &internal::kDefaultAsyncWaiter;
+ g_default_logger =
+ default_logger ? default_logger : &internal::kDefaultLogger;
+
+ g_default_task_tracker = default_task_tracker
+ ? default_task_tracker
+ : &internal::kDefaultTaskTracker;
+
+ RunLoop::SetUp();
+}
+
+} // namespace
+
+Environment::Environment() {
+ Init(nullptr, nullptr, nullptr);
+}
+
+Environment::Environment(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger,
+ const TaskTracker* default_task_tracker) {
+ Init(default_async_waiter, default_logger, default_task_tracker);
+}
+
+Environment::~Environment() {
+ RunLoop::TearDown();
+
+ // TODO(vtl): Maybe we should allow nesting, and restore previous default
+ // async waiters and loggers?
+ g_default_async_waiter = nullptr;
+ g_default_logger = nullptr;
+}
+
+// static
+const MojoAsyncWaiter* Environment::GetDefaultAsyncWaiter() {
+ assert(g_default_async_waiter); // Fails if not "inside" |Environment|.
+ return g_default_async_waiter;
+}
+
+// static
+const MojoLogger* Environment::GetDefaultLogger() {
+ assert(g_default_logger); // Fails if not "inside" |Environment|.
+ return g_default_logger;
+}
+
+// static
+const TaskTracker* Environment::GetDefaultTaskTracker() {
+ return g_default_task_tracker;
+}
+
+// static
+void Environment::InstantiateDefaultRunLoop() {
+ assert(!RunLoop::current());
+ // Not leaked: accessible from |RunLoop::current()|.
+ RunLoop* run_loop = new RunLoop();
+ MOJO_ALLOW_UNUSED_LOCAL(run_loop);
+ assert(run_loop == RunLoop::current());
+}
+
+// static
+void Environment::DestroyDefaultRunLoop() {
+ assert(RunLoop::current());
+ delete RunLoop::current();
+ assert(!RunLoop::current());
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/logging.cc b/mojo/public/cpp/environment/lib/logging.cc
new file mode 100644
index 0000000..57f1892
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/logging.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 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/environment/logging.h"
+
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+// Gets a pointer to the filename portion of |s|. Assumes that the filename
+// follows the last slash or backslash in |s|, or is |s| if no slash or
+// backslash is present.
+//
+// E.g., a pointer to "foo.cc" is returned for the following inputs: "foo.cc",
+// "./foo.cc", ".\foo.cc", "/absolute/path/to/foo.cc",
+// "relative/path/to/foo.cc", "C:\absolute\path\to\foo.cc", etc.
+const char* GetFilename(const char* s) {
+ const char* rv = s;
+ while (*s) {
+ if (*s == '/' || *s == '\\')
+ rv = s + 1;
+ s++;
+ }
+ return rv;
+}
+
+} // namespace
+
+// TODO(vtl): Maybe we should preserve the full path and strip it out at a
+// different level instead?
+LogMessage::LogMessage(MojoLogLevel log_level, const char* file, int line)
+ : log_level_(log_level), file_(GetFilename(file)), line_(line) {
+}
+
+LogMessage::~LogMessage() {
+ Environment::GetDefaultLogger()->LogMessage(
+ log_level_, file_, static_cast<uint32_t>(line_), stream_.str().c_str());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/scoped_task_tracking.cc b/mojo/public/cpp/environment/lib/scoped_task_tracking.cc
new file mode 100644
index 0000000..9e253d5
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/scoped_task_tracking.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 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/environment/lib/scoped_task_tracking.h"
+
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+ScopedTaskTracking::ScopedTaskTracking(const char* function_name,
+ const char* file_name,
+ int line,
+ const void* program_counter)
+ : id_(Environment::GetDefaultTaskTracker()->StartTracking(
+ function_name,
+ file_name,
+ line,
+ program_counter)) {
+}
+
+ScopedTaskTracking::ScopedTaskTracking(const char* function_name,
+ const char* file_name,
+ int line)
+ : id_(Environment::GetDefaultTaskTracker()->StartTracking(function_name,
+ file_name,
+ line,
+ nullptr)) {
+}
+
+ScopedTaskTracking::~ScopedTaskTracking() {
+ Environment::GetDefaultTaskTracker()->EndTracking(id_);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/lib/scoped_task_tracking.h b/mojo/public/cpp/environment/lib/scoped_task_tracking.h
new file mode 100644
index 0000000..b6e32c1
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/scoped_task_tracking.h
@@ -0,0 +1,34 @@
+// Copyright 2015 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_ENVIRONMENT_LIB_SCOPED_TASK_TRACKING_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_SCOPED_TASK_TRACKING_H_
+
+#include "mojo/public/cpp/environment/task_tracker.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// An RAII wrapper for |TaskTrackingId|.
+class ScopedTaskTracking {
+ public:
+ ScopedTaskTracking(const char* function_name,
+ const char* file_name,
+ int line,
+ const void* program_counter);
+ ScopedTaskTracking(const char* function_name,
+ const char* file_name,
+ int line);
+ ~ScopedTaskTracking();
+
+ private:
+ TaskTrackingId id_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedTaskTracking);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_SCOPED_TASK_TRACKING_H_
diff --git a/mojo/public/cpp/environment/logging.h b/mojo/public/cpp/environment/logging.h
new file mode 100644
index 0000000..bfc5c13
--- /dev/null
+++ b/mojo/public/cpp/environment/logging.h
@@ -0,0 +1,90 @@
+// Copyright 2014 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.
+
+// Logging macros, similar to Chromium's base/logging.h, except with |MOJO_|
+// prefixes and missing some features (notably |CHECK_EQ()|, etc.).
+
+// TODO(vtl): It's weird that this is in the environment directory, since its
+// implementation (in environment/lib) is meant to be used by any implementation
+// of the environment.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
+
+#include <sstream>
+
+#include "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+#define MOJO_LOG_STREAM(level) \
+ ::mojo::internal::LogMessage(MOJO_LOG_LEVEL_##level, __FILE__, __LINE__) \
+ .stream()
+
+#define MOJO_LAZY_LOG_STREAM(level, condition) \
+ !(condition) ? (void)0 \
+ : ::mojo::internal::VoidifyOstream() & MOJO_LOG_STREAM(level)
+
+#define MOJO_SHOULD_LOG(level) \
+ (MOJO_LOG_LEVEL_##level >= \
+ ::mojo::Environment::GetDefaultLogger()->GetMinimumLogLevel())
+
+#define MOJO_LOG(level) MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level))
+
+#define MOJO_LOG_IF(level, condition) \
+ MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level) && (condition))
+
+#define MOJO_CHECK(condition) \
+ MOJO_LAZY_LOG_STREAM(FATAL, !(condition)) << "Check failed: " #condition "." \
+ " "
+
+// Note: For non-debug builds, |MOJO_DLOG_IF()| *eliminates* (i.e., doesn't
+// compile) the condition, whereas |MOJO_DCHECK()| "neuters" the condition
+// (i.e., compiles, but doesn't evaluate).
+#ifdef NDEBUG
+#define MOJO_DLOG(level) MOJO_LAZY_LOG_STREAM(level, false)
+#define MOJO_DLOG_IF(level, condition) MOJO_LAZY_LOG_STREAM(level, false)
+#else
+#define MOJO_DLOG(level) MOJO_LOG(level)
+#define MOJO_DLOG_IF(level, condition) MOJO_LOG_IF(level, condition)
+#endif // NDEBUG
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define MOJO_DCHECK(condition) \
+ MOJO_LAZY_LOG_STREAM(FATAL, false ? !(condition) : false)
+#else
+#define MOJO_DCHECK(condition) MOJO_CHECK(condition)
+#endif // NDEBUG && !defined(DCHECK_ALWAYS_ON)
+
+#define MOJO_NOTREACHED() MOJO_DCHECK(false)
+
+namespace mojo {
+namespace internal {
+
+class LogMessage {
+ public:
+ LogMessage(MojoLogLevel log_level, const char* file, int line);
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ private:
+ const MojoLogLevel log_level_;
+ const char* const file_;
+ const int line_;
+ std::ostringstream stream_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Used to ignore a stream.
+struct VoidifyOstream {
+ // Use & since it has precedence lower than << but higher than ?:.
+ void operator&(std::ostream&) {}
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
diff --git a/mojo/public/cpp/environment/task_tracker.h b/mojo/public/cpp/environment/task_tracker.h
new file mode 100644
index 0000000..d6d3020
--- /dev/null
+++ b/mojo/public/cpp/environment/task_tracker.h
@@ -0,0 +1,30 @@
+// Copyright 2015 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_ENVIRONMENT_TASK_TRACKER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_TASK_TRACKER_H_
+
+#include <stdint.h>
+
+namespace mojo {
+
+typedef intptr_t TaskTrackingId;
+
+// Interface for wiring task-level profiling. This API is mainly used by the
+// generated interface implementation.
+struct TaskTracker {
+ // Start tracking. The returned id must be reclaimed through |EndTracking()|.
+ TaskTrackingId (*StartTracking)(const char* function_name,
+ const char* file_name,
+ int line_number,
+ const void* program_counter);
+ // Finish tracking. The |id| is one that is returned from |StartTracking()|.
+ void (*EndTracking)(const TaskTrackingId id);
+ // Enable or disable tracking. It is disabled by default.
+ void (*SetEnabled)(bool enabled);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_TASK_TRACKER_H_
diff --git a/mojo/public/cpp/environment/tests/BUILD.gn b/mojo/public/cpp/environment/tests/BUILD.gn
new file mode 100644
index 0000000..10fd056
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2014 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.
+
+import("../../../mojo_sdk.gni")
+
+mojo_sdk_source_set("tests") {
+ testonly = true
+
+ sources = [
+ "async_wait_unittest.cc",
+ "async_waiter_unittest.cc",
+ "logger_unittest.cc",
+ "logging_unittest.cc",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/c/environment",
+ "mojo/public/cpp/bindings:callback",
+ "mojo/public/cpp/environment:standalone",
+ "mojo/public/cpp/system",
+ "mojo/public/cpp/test_support:test_utils",
+ "mojo/public/cpp/utility",
+ ]
+}
diff --git a/mojo/public/cpp/environment/tests/async_wait_unittest.cc b/mojo/public/cpp/environment/tests/async_wait_unittest.cc
new file mode 100644
index 0000000..83c5ca0
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/async_wait_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2014 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 <string>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class TestAsyncWaitCallback {
+ public:
+ TestAsyncWaitCallback() : result_count_(0), last_result_(MOJO_RESULT_OK) {}
+ ~TestAsyncWaitCallback() {}
+
+ int result_count() const { return result_count_; }
+
+ MojoResult last_result() const { return last_result_; }
+
+ // MojoAsyncWaitCallback:
+ static void OnHandleReady(void* closure, MojoResult result) {
+ TestAsyncWaitCallback* self = static_cast<TestAsyncWaitCallback*>(closure);
+ self->result_count_++;
+ self->last_result_ = result;
+ }
+
+ private:
+ int result_count_;
+ MojoResult last_result_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback);
+};
+
+MojoAsyncWaitID CallAsyncWait(const Handle& handle,
+ MojoHandleSignals signals,
+ TestAsyncWaitCallback* callback) {
+ return Environment::GetDefaultAsyncWaiter()->AsyncWait(
+ handle.value(),
+ signals,
+ MOJO_DEADLINE_INDEFINITE,
+ &TestAsyncWaitCallback::OnHandleReady,
+ callback);
+}
+
+void CallCancelWait(MojoAsyncWaitID wait_id) {
+ Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id);
+}
+
+class AsyncWaitTest : public testing::Test {
+ public:
+ AsyncWaitTest() {}
+
+ private:
+ Environment environment_;
+ RunLoop run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaitTest);
+};
+
+// Verifies AsyncWaitCallback is notified when pipe is ready.
+TEST_F(AsyncWaitTest, CallbackNotified) {
+ TestAsyncWaitCallback callback;
+ MessagePipe test_pipe;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+ CallAsyncWait(
+ test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback);
+ RunLoop::current()->Run();
+ EXPECT_EQ(1, callback.result_count());
+ EXPECT_EQ(MOJO_RESULT_OK, callback.last_result());
+}
+
+// Verifies 2 AsyncWaitCallbacks are notified when there pipes are ready.
+TEST_F(AsyncWaitTest, TwoCallbacksNotified) {
+ TestAsyncWaitCallback callback1;
+ TestAsyncWaitCallback callback2;
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string()));
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string()));
+
+ CallAsyncWait(
+ test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback1);
+ CallAsyncWait(
+ test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback2);
+
+ RunLoop::current()->Run();
+ EXPECT_EQ(1, callback1.result_count());
+ EXPECT_EQ(MOJO_RESULT_OK, callback1.last_result());
+ EXPECT_EQ(1, callback2.result_count());
+ EXPECT_EQ(MOJO_RESULT_OK, callback2.last_result());
+}
+
+// Verifies cancel works.
+TEST_F(AsyncWaitTest, CancelCallback) {
+ TestAsyncWaitCallback callback;
+ MessagePipe test_pipe;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+ CallCancelWait(CallAsyncWait(
+ test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, &callback));
+ RunLoop::current()->Run();
+ EXPECT_EQ(0, callback.result_count());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/async_waiter_unittest.cc b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
new file mode 100644
index 0000000..1c1c2bf
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 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/callback.h"
+#include "mojo/public/cpp/environment/async_waiter.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class TestAsyncWaitCallback {
+ public:
+ TestAsyncWaitCallback() : result_count_(0), last_result_(MOJO_RESULT_OK) {}
+ ~TestAsyncWaitCallback() {}
+
+ int result_count() const { return result_count_; }
+
+ MojoResult last_result() const { return last_result_; }
+
+ void OnHandleReady(MojoResult result) {
+ result_count_++;
+ last_result_ = result;
+ }
+
+ private:
+ int result_count_;
+ MojoResult last_result_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestAsyncWaitCallback);
+};
+
+// Manual code to create a callback since we don't have mojo::Bind yet.
+class ManualCallback {
+ public:
+ explicit ManualCallback(TestAsyncWaitCallback* callback)
+ : callback_(callback) {}
+
+ void Run(MojoResult result) const { callback_->OnHandleReady(result); }
+
+ private:
+ TestAsyncWaitCallback* callback_;
+};
+
+class AsyncWaiterTest : public testing::Test {
+ public:
+ AsyncWaiterTest() {}
+
+ private:
+ Environment environment_;
+ RunLoop run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterTest);
+};
+
+// Verifies AsyncWaitCallback is notified when pipe is ready.
+TEST_F(AsyncWaiterTest, CallbackNotified) {
+ TestAsyncWaitCallback callback;
+ MessagePipe test_pipe;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+ AsyncWaiter waiter(test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ ManualCallback(&callback));
+ RunLoop::current()->Run();
+ EXPECT_EQ(1, callback.result_count());
+ EXPECT_EQ(MOJO_RESULT_OK, callback.last_result());
+}
+
+// Verifies 2 AsyncWaitCallbacks are notified when there pipes are ready.
+TEST_F(AsyncWaiterTest, TwoCallbacksNotified) {
+ TestAsyncWaitCallback callback1;
+ TestAsyncWaitCallback callback2;
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe1.handle1.get(), std::string()));
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe2.handle1.get(), std::string()));
+
+ AsyncWaiter waiter1(test_pipe1.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ ManualCallback(&callback1));
+ AsyncWaiter waiter2(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ ManualCallback(&callback2));
+
+ RunLoop::current()->Run();
+ EXPECT_EQ(1, callback1.result_count());
+ EXPECT_EQ(MOJO_RESULT_OK, callback1.last_result());
+ EXPECT_EQ(1, callback2.result_count());
+ EXPECT_EQ(MOJO_RESULT_OK, callback2.last_result());
+}
+
+// Verifies cancel works.
+TEST_F(AsyncWaiterTest, CancelCallback) {
+ TestAsyncWaitCallback callback;
+ MessagePipe test_pipe;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+ {
+ AsyncWaiter waiter(test_pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ ManualCallback(&callback));
+ }
+ RunLoop::current()->Run();
+ EXPECT_EQ(0, callback.result_count());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/logger_unittest.cc b/mojo/public/cpp/environment/tests/logger_unittest.cc
new file mode 100644
index 0000000..8ca5538
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/logger_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2014 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/c/environment/logger.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(LoggerTest, Basic) {
+ const char kPath[] = "/fake/path/to/file.cc";
+
+ Environment environment;
+ const MojoLogger* const logger = Environment::GetDefaultLogger();
+
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, kPath, 123,
+ "Logged at VERBOSE-1 level");
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, kPath, 123,
+ "Logged at VERBOSE level");
+ logger->LogMessage(MOJO_LOG_LEVEL_INFO, kPath, 123, "Logged at INFO level");
+ logger->LogMessage(MOJO_LOG_LEVEL_WARNING, kPath, 123,
+ "Logged at WARNING level");
+ logger->LogMessage(MOJO_LOG_LEVEL_ERROR, kPath, 123, "Logged at ERROR level");
+
+ // This should kill us:
+ EXPECT_DEATH_IF_SUPPORTED({
+ logger->LogMessage(MOJO_LOG_LEVEL_FATAL, kPath, 123,
+ "Logged at FATAL level");
+ }, "");
+}
+
+TEST(LoggerTest, LogLevels) {
+ const char kPath[] = "/fake/path/to/file.cc";
+
+ Environment environment;
+ const MojoLogger* const logger = Environment::GetDefaultLogger();
+
+ for (MojoLogLevel log_level = MOJO_LOG_LEVEL_VERBOSE - 1;
+ log_level <= MOJO_LOG_LEVEL_FATAL + 1;
+ log_level++) {
+ logger->SetMinimumLogLevel(log_level);
+
+ if (log_level <= MOJO_LOG_LEVEL_FATAL)
+ EXPECT_EQ(log_level, logger->GetMinimumLogLevel());
+ else
+ EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, logger->GetMinimumLogLevel());
+
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, kPath, 123,
+ "Logged at VERBOSE-1 level");
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, kPath, 123,
+ "Logged at VERBOSE level");
+ logger->LogMessage(MOJO_LOG_LEVEL_INFO, kPath, 123, "Logged at INFO level");
+ logger->LogMessage(MOJO_LOG_LEVEL_WARNING, kPath, 123,
+ "Logged at WARNING level");
+ logger->LogMessage(MOJO_LOG_LEVEL_ERROR, kPath, 123,
+ "Logged at ERROR level");
+
+ // This should kill us:
+ EXPECT_DEATH_IF_SUPPORTED({
+ logger->LogMessage(MOJO_LOG_LEVEL_FATAL, kPath, 123,
+ "Logged at FATAL level");
+ }, "");
+ }
+}
+
+TEST(LoggerTest, NoFile) {
+ Environment environment;
+ const MojoLogger* const logger = Environment::GetDefaultLogger();
+
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, nullptr, 0,
+ "Logged at VERBOSE-1 level");
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, nullptr, 0,
+ "Logged at VERBOSE level");
+ logger->LogMessage(MOJO_LOG_LEVEL_INFO, nullptr, 0, "Logged at INFO level");
+ logger->LogMessage(MOJO_LOG_LEVEL_WARNING, nullptr, 0,
+ "Logged at WARNING level");
+ logger->LogMessage(MOJO_LOG_LEVEL_ERROR, nullptr, 0, "Logged at ERROR level");
+
+ // This should kill us:
+ EXPECT_DEATH_IF_SUPPORTED({
+ logger->LogMessage(MOJO_LOG_LEVEL_FATAL, nullptr, 0,
+ "Logged at FATAL level");
+ }, "");
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/tests/logging_unittest.cc b/mojo/public/cpp/environment/tests/logging_unittest.cc
new file mode 100644
index 0000000..489888c
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/logging_unittest.cc
@@ -0,0 +1,521 @@
+// Copyright 2014 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 <stdlib.h>
+
+#include <sstream>
+#include <string>
+
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+// The current logging system strips the path, so we need our filename.
+const char kOurFilename[] = "logging_unittest.cc";
+
+class PtrToMemberHelper {
+ public:
+ int member;
+};
+
+bool DcheckTestHelper(bool* was_called) {
+ *was_called = true;
+ return false;
+}
+
+class LoggingTest : public testing::Test {
+ public:
+ LoggingTest() : environment_(nullptr, &kMockLogger, nullptr) {
+ minimum_log_level_ = MOJO_LOG_LEVEL_INFO;
+ ResetMockLogger();
+ }
+ ~LoggingTest() override {}
+
+ protected:
+ // Note: Does not reset |minimum_log_level_|.
+ static void ResetMockLogger() {
+ log_message_was_called_ = false;
+ last_log_level_ = MOJO_LOG_LEVEL_INFO;
+ last_source_file_.clear();
+ last_source_line_ = 0;
+ last_message_.clear();
+ }
+
+ // A function returning |bool| that shouldn't be called.
+ static bool NotCalledCondition() {
+ not_called_condition_was_called_ = true;
+ return false;
+ }
+
+ static bool log_message_was_called() { return log_message_was_called_; }
+ static MojoLogLevel last_log_level() { return last_log_level_; }
+ static const std::string& last_source_file() { return last_source_file_; }
+ static uint32_t last_source_line() { return last_source_line_; }
+ static const std::string& last_message() { return last_message_; }
+ static bool not_called_condition_was_called() {
+ return not_called_condition_was_called_;
+ }
+
+ private:
+ // Note: We record calls even if |log_level| is below |minimum_log_level_|
+ // (since the macros should mostly avoid this, and we want to be able to check
+ // that they do).
+ static void MockLogMessage(MojoLogLevel log_level,
+ const char* source_file,
+ uint32_t source_line,
+ const char* message) {
+ log_message_was_called_ = true;
+ last_log_level_ = log_level;
+ last_source_file_ = source_file;
+ last_source_line_ = source_line;
+ last_message_ = message;
+ }
+
+ static MojoLogLevel MockGetMinimumLogLevel() { return minimum_log_level_; }
+
+ static void MockSetMinimumLogLevel(MojoLogLevel minimum_log_level) {
+ minimum_log_level_ = minimum_log_level;
+ }
+
+ Environment environment_;
+
+ static const MojoLogger kMockLogger;
+ static MojoLogLevel minimum_log_level_;
+ static bool log_message_was_called_;
+ static MojoLogLevel last_log_level_;
+ static std::string last_source_file_;
+ static uint32_t last_source_line_;
+ static std::string last_message_;
+ static bool not_called_condition_was_called_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(LoggingTest);
+};
+
+// static
+const MojoLogger LoggingTest::kMockLogger = {
+ &LoggingTest::MockLogMessage,
+ &LoggingTest::MockGetMinimumLogLevel,
+ &LoggingTest::MockSetMinimumLogLevel};
+
+// static
+MojoLogLevel LoggingTest::minimum_log_level_ = MOJO_LOG_LEVEL_INFO;
+
+// static
+bool LoggingTest::log_message_was_called_ = MOJO_LOG_LEVEL_INFO;
+
+// static
+MojoLogLevel LoggingTest::last_log_level_ = MOJO_LOG_LEVEL_INFO;
+
+// static
+std::string LoggingTest::last_source_file_;
+
+// static
+uint32_t LoggingTest::last_source_line_ = 0;
+
+// static
+std::string LoggingTest::last_message_;
+
+// static
+bool LoggingTest::not_called_condition_was_called_ = false;
+
+TEST_F(LoggingTest, InternalLogMessage) {
+ internal::LogMessage(MOJO_LOG_LEVEL_INFO, "foo.cc", 123).stream() << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_WARNING, "./path/to/foo.cc", 123).stream()
+ << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_ERROR, "/path/to/foo.cc", 123).stream()
+ << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_FATAL, "path/to/foo.cc", 123).stream()
+ << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_VERBOSE, ".\\xy\\foo.cc", 123).stream()
+ << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_VERBOSE - 1, "xy\\foo.cc", 123).stream()
+ << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE - 1, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_VERBOSE - 9, "C:\\xy\\foo.cc", 123)
+ .stream()
+ << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE - 9, last_log_level());
+ EXPECT_EQ("foo.cc", last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(MOJO_LOG_LEVEL_INFO, __FILE__, 123).stream() << "hello "
+ << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(123u, last_source_line());
+ EXPECT_EQ("hello world", last_message());
+}
+
+TEST_F(LoggingTest, LogStream) {
+ MOJO_LOG_STREAM(INFO) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hello", last_message());
+
+ ResetMockLogger();
+
+ MOJO_LOG_STREAM(ERROR) << "hi " << 123;
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hi 123", last_message());
+}
+
+TEST_F(LoggingTest, LazyLogStream) {
+ MOJO_LAZY_LOG_STREAM(INFO, true) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hello", last_message());
+
+ ResetMockLogger();
+
+ MOJO_LAZY_LOG_STREAM(ERROR, true) << "hi " << 123;
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hi 123", last_message());
+
+ ResetMockLogger();
+
+ MOJO_LAZY_LOG_STREAM(INFO, false) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LAZY_LOG_STREAM(FATAL, false) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ PtrToMemberHelper helper;
+ helper.member = 1;
+ int PtrToMemberHelper::*member_ptr = &PtrToMemberHelper::member;
+
+ // This probably fails to compile if we forget to parenthesize the condition
+ // in the macro (.* has lower precedence than !, which can't apply to
+ // |helper|).
+ MOJO_LAZY_LOG_STREAM(ERROR, helper.*member_ptr == 1) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LAZY_LOG_STREAM(WARNING, helper.*member_ptr == 0) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+}
+
+TEST_F(LoggingTest, ShouldLog) {
+ // We start at |MOJO_LOG_LEVEL_INFO|.
+ EXPECT_FALSE(MOJO_SHOULD_LOG(VERBOSE));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(INFO));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(WARNING));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL));
+
+ Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR);
+ EXPECT_FALSE(MOJO_SHOULD_LOG(VERBOSE));
+ EXPECT_FALSE(MOJO_SHOULD_LOG(INFO));
+ EXPECT_FALSE(MOJO_SHOULD_LOG(WARNING));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL));
+
+ Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_VERBOSE -
+ 1);
+ EXPECT_TRUE(MOJO_SHOULD_LOG(VERBOSE));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(INFO));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(WARNING));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(ERROR));
+ EXPECT_TRUE(MOJO_SHOULD_LOG(FATAL));
+}
+
+TEST_F(LoggingTest, Log) {
+ // We start at |MOJO_LOG_LEVEL_INFO|.
+ MOJO_LOG(VERBOSE) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG(INFO) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hello", last_message());
+
+ ResetMockLogger();
+
+ MOJO_LOG(ERROR) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hello", last_message());
+
+ ResetMockLogger();
+
+ Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR);
+
+ MOJO_LOG(VERBOSE) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG(INFO) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG(ERROR) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hello", last_message());
+}
+
+TEST_F(LoggingTest, LogIf) {
+ // We start at |MOJO_LOG_LEVEL_INFO|.
+ MOJO_LOG_IF(VERBOSE, true) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG_IF(VERBOSE, false) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+ Environment::GetDefaultLogger()->SetMinimumLogLevel(MOJO_LOG_LEVEL_ERROR);
+
+ bool x = true;
+ // Also try to make sure that we parenthesize the condition properly.
+ MOJO_LOG_IF(INFO, false || x) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG_IF(INFO, 0 != 1) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG_IF(WARNING, 1 + 1 == 2) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_LOG_IF(ERROR, 1 * 2 == 2) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("hello", last_message());
+
+ ResetMockLogger();
+
+ MOJO_LOG_IF(FATAL, 1 * 2 == 3) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ // |MOJO_LOG_IF()| shouldn't evaluate its condition if the level is below the
+ // minimum.
+ MOJO_LOG_IF(INFO, NotCalledCondition()) << "hello";
+ EXPECT_FALSE(not_called_condition_was_called());
+ EXPECT_FALSE(log_message_was_called());
+}
+
+TEST_F(LoggingTest, Check) {
+ MOJO_CHECK(true) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ PtrToMemberHelper helper;
+ helper.member = 0;
+ int PtrToMemberHelper::*member_ptr = &PtrToMemberHelper::member;
+
+ // Also try to make sure that we parenthesize the condition properly.
+ MOJO_CHECK(helper.*member_ptr == 1) << "hello";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 4), last_source_line());
+ EXPECT_EQ("Check failed: helper.*member_ptr == 1. hello", last_message());
+
+ ResetMockLogger();
+
+ // Also test a "naked" |MOJO_CHECK()|s.
+ MOJO_CHECK(1 + 2 == 3);
+ EXPECT_FALSE(log_message_was_called());
+}
+
+TEST_F(LoggingTest, Dlog) {
+ // We start at |MOJO_LOG_LEVEL_INFO|.
+ MOJO_DLOG(VERBOSE) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_DLOG(INFO) << "hello";
+#ifdef NDEBUG
+ EXPECT_FALSE(log_message_was_called());
+#else
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 7), last_source_line());
+ EXPECT_EQ("hello", last_message());
+#endif
+}
+
+TEST_F(LoggingTest, DlogIf) {
+ // We start at |MOJO_LOG_LEVEL_INFO|. It shouldn't evaluate the condition in
+ // this case.
+ MOJO_DLOG_IF(VERBOSE, NotCalledCondition()) << "hello";
+ EXPECT_FALSE(not_called_condition_was_called());
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_DLOG_IF(INFO, 1 == 0) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_DLOG_IF(INFO, 1 == 1) << "hello";
+#ifdef NDEBUG
+ EXPECT_FALSE(log_message_was_called());
+#else
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 7), last_source_line());
+ EXPECT_EQ("hello", last_message());
+#endif
+
+ ResetMockLogger();
+
+// |MOJO_DLOG_IF()| shouldn't compile its condition for non-debug builds.
+#ifndef NDEBUG
+ bool debug_only = true;
+#endif
+ MOJO_DLOG_IF(WARNING, debug_only) << "hello";
+#ifdef NDEBUG
+ EXPECT_FALSE(log_message_was_called());
+#else
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 7), last_source_line());
+ EXPECT_EQ("hello", last_message());
+#endif
+}
+
+TEST_F(LoggingTest, Dcheck) {
+ MOJO_DCHECK(true);
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ MOJO_DCHECK(true) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+
+ ResetMockLogger();
+
+ // |MOJO_DCHECK()| should compile (but not evaluate) its condition even for
+ // non-debug builds. (Hopefully, we'll get an unused variable error if it
+ // fails to compile the condition.)
+ bool was_called = false;
+ MOJO_DCHECK(DcheckTestHelper(&was_called)) << "hello";
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+ EXPECT_FALSE(was_called);
+ EXPECT_FALSE(log_message_was_called());
+#else
+ EXPECT_TRUE(was_called);
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+ EXPECT_EQ(kOurFilename, last_source_file());
+ EXPECT_EQ(static_cast<uint32_t>(__LINE__ - 9), last_source_line());
+ EXPECT_EQ("Check failed: DcheckTestHelper(&was_called). hello",
+ last_message());
+#endif
+
+ ResetMockLogger();
+
+ // Also try to make sure that we parenthesize the condition properly.
+ bool x = true;
+ MOJO_DCHECK(false || x) << "hello";
+ EXPECT_FALSE(log_message_was_called());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
new file mode 100644
index 0000000..e120462
--- /dev/null
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2014 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.
+
+import("../../mojo_sdk.gni")
+
+mojo_sdk_source_set("system") {
+ sources = [
+ "buffer.h",
+ "core.h",
+ "data_pipe.h",
+ "functions.h",
+ "handle.h",
+ "macros.h",
+ "message_pipe.h",
+ ]
+
+ mojo_sdk_public_deps = [ "mojo/public/c/system" ]
+}
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
new file mode 100644
index 0000000..9458c0a
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.h
@@ -0,0 +1,132 @@
+// Copyright 2014 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.
+
+// This file provides a C++ wrapping around the Mojo C API for shared buffers,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/buffer.h" for complete documentation of the
+// API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| referring to a shared
+// buffer.
+class SharedBufferHandle : public Handle {
+ public:
+ SharedBufferHandle() {}
+ explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(SharedBufferHandle) == sizeof(Handle),
+ "Bad size for C++ SharedBufferHandle");
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
+static_assert(sizeof(ScopedSharedBufferHandle) == sizeof(SharedBufferHandle),
+ "Bad size for C++ ScopedSharedBufferHandle");
+
+// Creates a shared buffer. See |MojoCreateSharedBuffer()| for complete
+// documentation.
+inline MojoResult CreateSharedBuffer(
+ const MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ ScopedSharedBufferHandle* shared_buffer) {
+ assert(shared_buffer);
+ SharedBufferHandle handle;
+ MojoResult rv =
+ MojoCreateSharedBuffer(options, num_bytes, handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ shared_buffer->reset(handle);
+ return rv;
+}
+
+// Duplicates a handle to a buffer, most commonly so that the buffer can be
+// shared with other applications. See |MojoDuplicateBufferHandle()| for
+// complete documentation.
+//
+// TODO(ggowan): Rename this to DuplicateBufferHandle since it is making another
+// handle to the same buffer, not duplicating the buffer itself.
+//
+// TODO(vtl): This (and also the functions below) are templatized to allow for
+// future/other buffer types. A bit "safer" would be to overload this function
+// manually. (The template enforces that the in and out handles be of the same
+// type.)
+template <class BufferHandleType>
+inline MojoResult DuplicateBuffer(
+ BufferHandleType buffer,
+ const MojoDuplicateBufferHandleOptions* options,
+ ScopedHandleBase<BufferHandleType>* new_buffer) {
+ assert(new_buffer);
+ BufferHandleType handle;
+ MojoResult rv = MojoDuplicateBufferHandle(
+ buffer.value(), options, handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ new_buffer->reset(handle);
+ return rv;
+}
+
+// Maps a part of a buffer (specified by |buffer|, |offset|, and |num_bytes|)
+// into memory. See |MojoMapBuffer()| for complete documentation.
+template <class BufferHandleType>
+inline MojoResult MapBuffer(BufferHandleType buffer,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** pointer,
+ MojoMapBufferFlags flags) {
+ assert(buffer.is_valid());
+ return MojoMapBuffer(buffer.value(), offset, num_bytes, pointer, flags);
+}
+
+// Unmaps a part of a buffer that was previously mapped with |MapBuffer()|.
+// See |MojoUnmapBuffer()| for complete documentation.
+inline MojoResult UnmapBuffer(void* pointer) {
+ assert(pointer);
+ return MojoUnmapBuffer(pointer);
+}
+
+// A wrapper class that automatically creates a shared buffer and owns the
+// handle.
+class SharedBuffer {
+ public:
+ explicit SharedBuffer(uint64_t num_bytes);
+ SharedBuffer(uint64_t num_bytes,
+ const MojoCreateSharedBufferOptions& options);
+ ~SharedBuffer();
+
+ ScopedSharedBufferHandle handle;
+};
+
+inline SharedBuffer::SharedBuffer(uint64_t num_bytes) {
+ MojoResult result = CreateSharedBuffer(nullptr, num_bytes, &handle);
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::SharedBuffer(
+ uint64_t num_bytes,
+ const MojoCreateSharedBufferOptions& options) {
+ MojoResult result = CreateSharedBuffer(&options, num_bytes, &handle);
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::~SharedBuffer() {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_
diff --git a/mojo/public/cpp/system/core.h b/mojo/public/cpp/system/core.h
new file mode 100644
index 0000000..b08a5a6
--- /dev/null
+++ b/mojo/public/cpp/system/core.h
@@ -0,0 +1,15 @@
+// Copyright 2014 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_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/functions.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
diff --git a/mojo/public/cpp/system/data_pipe.h b/mojo/public/cpp/system/data_pipe.h
new file mode 100644
index 0000000..c451cff
--- /dev/null
+++ b/mojo/public/cpp/system/data_pipe.h
@@ -0,0 +1,162 @@
+// Copyright 2014 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.
+
+// This file provides a C++ wrapping around the Mojo C API for data pipes,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/data_pipe.h" for complete documentation of
+// the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| to the producer end of a
+// data pipe.
+class DataPipeProducerHandle : public Handle {
+ public:
+ DataPipeProducerHandle() {}
+ explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(DataPipeProducerHandle) == sizeof(Handle),
+ "Bad size for C++ DataPipeProducerHandle");
+
+typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
+static_assert(sizeof(ScopedDataPipeProducerHandle) ==
+ sizeof(DataPipeProducerHandle),
+ "Bad size for C++ ScopedDataPipeProducerHandle");
+
+// A strongly-typed representation of a |MojoHandle| to the consumer end of a
+// data pipe.
+class DataPipeConsumerHandle : public Handle {
+ public:
+ DataPipeConsumerHandle() {}
+ explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
+ "Bad size for C++ DataPipeConsumerHandle");
+
+typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
+static_assert(sizeof(ScopedDataPipeConsumerHandle) ==
+ sizeof(DataPipeConsumerHandle),
+ "Bad size for C++ ScopedDataPipeConsumerHandle");
+
+// Creates a new data pipe. See |MojoCreateDataPipe()| for complete
+// documentation.
+inline MojoResult CreateDataPipe(
+ const MojoCreateDataPipeOptions* options,
+ ScopedDataPipeProducerHandle* data_pipe_producer,
+ ScopedDataPipeConsumerHandle* data_pipe_consumer) {
+ assert(data_pipe_producer);
+ assert(data_pipe_consumer);
+ DataPipeProducerHandle producer_handle;
+ DataPipeConsumerHandle consumer_handle;
+ MojoResult rv = MojoCreateDataPipe(options,
+ producer_handle.mutable_value(),
+ consumer_handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ data_pipe_producer->reset(producer_handle);
+ data_pipe_consumer->reset(consumer_handle);
+ return rv;
+}
+
+// Writes to a data pipe. See |MojoWriteData| for complete documentation.
+inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) {
+ return MojoWriteData(data_pipe_producer.value(), elements, num_bytes, flags);
+}
+
+// Begins a two-phase write to a data pipe. See |MojoBeginWriteData()| for
+// complete documentation.
+inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) {
+ return MojoBeginWriteData(
+ data_pipe_producer.value(), buffer, buffer_num_bytes, flags);
+}
+
+// Completes a two-phase write to a data pipe. See |MojoEndWriteData()| for
+// complete documentation.
+inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ uint32_t num_bytes_written) {
+ return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written);
+}
+
+// Reads from a data pipe. See |MojoReadData()| for complete documentation.
+inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags) {
+ return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags);
+}
+
+// Begins a two-phase read from a data pipe. See |MojoBeginReadData()| for
+// complete documentation.
+inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) {
+ return MojoBeginReadData(
+ data_pipe_consumer.value(), buffer, buffer_num_bytes, flags);
+}
+
+// Completes a two-phase read from a data pipe. See |MojoEndReadData()| for
+// complete documentation.
+inline MojoResult EndReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ uint32_t num_bytes_read) {
+ return MojoEndReadData(data_pipe_consumer.value(), num_bytes_read);
+}
+
+// A wrapper class that automatically creates a data pipe and owns both handles.
+// TODO(vtl): Make an even more friendly version? (Maybe templatized for a
+// particular type instead of some "element"? Maybe functions that take
+// vectors?)
+class DataPipe {
+ public:
+ DataPipe();
+ explicit DataPipe(const MojoCreateDataPipeOptions& options);
+ ~DataPipe();
+
+ ScopedDataPipeProducerHandle producer_handle;
+ ScopedDataPipeConsumerHandle consumer_handle;
+};
+
+inline DataPipe::DataPipe() {
+ MojoResult result =
+ CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
+ MojoResult result =
+ CreateDataPipe(&options, &producer_handle, &consumer_handle);
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::~DataPipe() {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_
diff --git a/mojo/public/cpp/system/functions.h b/mojo/public/cpp/system/functions.h
new file mode 100644
index 0000000..9cfe316
--- /dev/null
+++ b/mojo/public/cpp/system/functions.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+// This file provides a C++ wrapping around the standalone functions of the Mojo
+// C API, replacing the prefix of "Mojo" with a "mojo" namespace.
+//
+// Please see "mojo/public/c/system/functions.h" for complete documentation of
+// the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+// Returns the current |MojoTimeTicks| value. See |MojoGetTimeTicksNow()| for
+// complete documentation.
+inline MojoTimeTicks GetTimeTicksNow() {
+ return MojoGetTimeTicksNow();
+}
+
+// The C++ wrappers for |MojoWait()| and |MojoWaitMany()| are defined in
+// "handle.h".
+// TODO(ggowan): Consider making the C and C++ APIs more consistent in the
+// organization of the functions into different header files (since in the C
+// API, those functions are defined in "functions.h").
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h
new file mode 100644
index 0000000..45624bc
--- /dev/null
+++ b/mojo/public/cpp/system/handle.h
@@ -0,0 +1,290 @@
+// Copyright 2014 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_SYSTEM_HANDLE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
+
+#include <assert.h>
+#include <limits>
+
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// OVERVIEW
+//
+// |Handle| and |...Handle|:
+//
+// |Handle| is a simple, copyable wrapper for the C type |MojoHandle| (which is
+// just an integer). Its purpose is to increase type-safety, not provide
+// lifetime management. For the same purpose, we have trivial *subclasses* of
+// |Handle|, e.g., |MessagePipeHandle| and |DataPipeProducerHandle|. |Handle|
+// and its subclasses impose *no* extra overhead over using |MojoHandle|s
+// directly.
+//
+// Note that though we provide constructors for |Handle|/|...Handle| from a
+// |MojoHandle|, we do not provide, e.g., a constructor for |MessagePipeHandle|
+// from a |Handle|. This is for type safety: If we did, you'd then be able to
+// construct a |MessagePipeHandle| from, e.g., a |DataPipeProducerHandle| (since
+// it's a |Handle|).
+//
+// |ScopedHandleBase| and |Scoped...Handle|:
+//
+// |ScopedHandleBase<HandleType>| is a templated scoped wrapper, for the handle
+// types above (in the same sense that a C++11 |unique_ptr<T>| is a scoped
+// wrapper for a |T*|). It provides lifetime management, closing its owned
+// handle on destruction. It also provides (emulated) move semantics, again
+// along the lines of C++11's |unique_ptr| (and exactly like Chromium's
+// |scoped_ptr|).
+//
+// |ScopedHandle| is just (a typedef of) a |ScopedHandleBase<Handle>|.
+// Similarly, |ScopedMessagePipeHandle| is just a
+// |ScopedHandleBase<MessagePipeHandle>|. Etc. Note that a
+// |ScopedMessagePipeHandle| is *not* a (subclass of) |ScopedHandle|.
+//
+// Wrapper functions:
+//
+// We provide simple wrappers for the |Mojo...()| functions (in
+// mojo/public/c/system/core.h -- see that file for details on individual
+// functions).
+//
+// The general guideline is functions that imply ownership transfer of a handle
+// should take (or produce) an appropriate |Scoped...Handle|, while those that
+// don't take a |...Handle|. For example, |CreateMessagePipe()| has two
+// |ScopedMessagePipe| "out" parameters, whereas |Wait()| and |WaitMany()| take
+// |Handle| parameters. Some, have both: e.g., |DuplicatedBuffer()| takes a
+// suitable (unscoped) handle (e.g., |SharedBufferHandle|) "in" parameter and
+// produces a suitable scoped handle (e.g., |ScopedSharedBufferHandle| a.k.a.
+// |ScopedHandleBase<SharedBufferHandle>|) as an "out" parameter.
+//
+// An exception are some of the |...Raw()| functions. E.g., |CloseRaw()| takes a
+// |Handle|, leaving the user to discard the wrapper.
+//
+// ScopedHandleBase ------------------------------------------------------------
+
+// Scoper for the actual handle types defined further below. It's move-only,
+// like the C++11 |unique_ptr|.
+template <class HandleType>
+class ScopedHandleBase {
+ MOJO_MOVE_ONLY_TYPE(ScopedHandleBase)
+
+ public:
+ ScopedHandleBase() {}
+ explicit ScopedHandleBase(HandleType handle) : handle_(handle) {}
+ ~ScopedHandleBase() { CloseIfNecessary(); }
+
+ template <class CompatibleHandleType>
+ explicit ScopedHandleBase(ScopedHandleBase<CompatibleHandleType> other)
+ : handle_(other.release()) {}
+
+ // Move-only constructor and operator=.
+ ScopedHandleBase(ScopedHandleBase&& other) : handle_(other.release()) {}
+ ScopedHandleBase& operator=(ScopedHandleBase&& other) {
+ if (&other != this) {
+ CloseIfNecessary();
+ handle_ = other.release();
+ }
+ return *this;
+ }
+
+ const HandleType& get() const { return handle_; }
+
+ template <typename PassedHandleType>
+ static ScopedHandleBase<HandleType> From(
+ ScopedHandleBase<PassedHandleType> other) {
+ static_assert(
+ sizeof(static_cast<PassedHandleType*>(static_cast<HandleType*>(0))),
+ "HandleType is not a subtype of PassedHandleType");
+ return ScopedHandleBase<HandleType>(
+ static_cast<HandleType>(other.release().value()));
+ }
+
+ void swap(ScopedHandleBase& other) { handle_.swap(other.handle_); }
+
+ HandleType release() MOJO_WARN_UNUSED_RESULT {
+ HandleType rv;
+ rv.swap(handle_);
+ return rv;
+ }
+
+ void reset(HandleType handle = HandleType()) {
+ CloseIfNecessary();
+ handle_ = handle;
+ }
+
+ bool is_valid() const { return handle_.is_valid(); }
+
+ private:
+ void CloseIfNecessary() {
+ if (!handle_.is_valid())
+ return;
+ MojoResult result = MojoClose(handle_.value());
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+ }
+
+ HandleType handle_;
+};
+
+template <typename HandleType>
+inline ScopedHandleBase<HandleType> MakeScopedHandle(HandleType handle) {
+ return ScopedHandleBase<HandleType>(handle);
+}
+
+// Handle ----------------------------------------------------------------------
+
+const MojoHandle kInvalidHandleValue = MOJO_HANDLE_INVALID;
+
+// Wrapper base class for |MojoHandle|.
+class Handle {
+ public:
+ Handle() : value_(kInvalidHandleValue) {}
+ explicit Handle(MojoHandle value) : value_(value) {}
+ ~Handle() {}
+
+ void swap(Handle& other) {
+ MojoHandle temp = value_;
+ value_ = other.value_;
+ other.value_ = temp;
+ }
+
+ bool is_valid() const { return value_ != kInvalidHandleValue; }
+
+ const MojoHandle& value() const { return value_; }
+ MojoHandle* mutable_value() { return &value_; }
+ void set_value(MojoHandle value) { value_ = value; }
+
+ private:
+ MojoHandle value_;
+
+ // Copying and assignment allowed.
+};
+
+// Should have zero overhead.
+static_assert(sizeof(Handle) == sizeof(MojoHandle), "Bad size for C++ Handle");
+
+// The scoper should also impose no more overhead.
+typedef ScopedHandleBase<Handle> ScopedHandle;
+static_assert(sizeof(ScopedHandle) == sizeof(Handle),
+ "Bad size for C++ ScopedHandle");
+
+inline MojoResult Wait(Handle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ MojoHandleSignalsState* signals_state) {
+ return MojoWait(handle.value(), signals, deadline, signals_state);
+}
+
+const uint32_t kInvalidWaitManyIndexValue = static_cast<uint32_t>(-1);
+
+// Simplify the interpretation of the output from |MojoWaitMany()|.
+class WaitManyResult {
+ public:
+ explicit WaitManyResult(MojoResult mojo_wait_many_result)
+ : result(mojo_wait_many_result), index(kInvalidWaitManyIndexValue) {}
+
+ WaitManyResult(MojoResult mojo_wait_many_result, uint32_t result_index)
+ : result(mojo_wait_many_result), index(result_index) {}
+
+ // A valid handle index is always returned if |WaitMany()| succeeds, but may
+ // or may not be returned if |WaitMany()| returns an error. Use this helper
+ // function to check if |index| is a valid index into the handle array.
+ bool IsIndexValid() const { return index != kInvalidWaitManyIndexValue; }
+
+ // The |signals_states| array is always returned by |WaitMany()| on success,
+ // but may or may not be returned if |WaitMany()| returns an error. Use this
+ // helper function to check if |signals_states| holds valid data.
+ bool AreSignalsStatesValid() const {
+ return result != MOJO_RESULT_INVALID_ARGUMENT &&
+ result != MOJO_RESULT_RESOURCE_EXHAUSTED;
+ }
+
+ MojoResult result;
+ uint32_t index;
+};
+
+// |HandleVectorType| and |FlagsVectorType| should be similar enough to
+// |std::vector<Handle>| and |std::vector<MojoHandleSignals>|, respectively:
+// - They should have a (const) |size()| method that returns an unsigned type.
+// - They must provide contiguous storage, with access via (const) reference to
+// that storage provided by a (const) |operator[]()| (by reference).
+template <class HandleVectorType,
+ class FlagsVectorType,
+ class SignalsStateVectorType>
+inline WaitManyResult WaitMany(const HandleVectorType& handles,
+ const FlagsVectorType& signals,
+ MojoDeadline deadline,
+ SignalsStateVectorType* signals_states) {
+ if (signals.size() != handles.size() ||
+ (signals_states && signals_states->size() != signals.size()))
+ return WaitManyResult(MOJO_RESULT_INVALID_ARGUMENT);
+ if (handles.size() >= kInvalidWaitManyIndexValue)
+ return WaitManyResult(MOJO_RESULT_RESOURCE_EXHAUSTED);
+
+ if (handles.size() == 0) {
+ return WaitManyResult(
+ MojoWaitMany(nullptr, nullptr, 0, deadline, nullptr, nullptr));
+ }
+
+ uint32_t result_index = kInvalidWaitManyIndexValue;
+ const Handle& first_handle = handles[0];
+ const MojoHandleSignals& first_signals = signals[0];
+ MojoHandleSignalsState* first_state =
+ signals_states ? &(*signals_states)[0] : nullptr;
+ MojoResult result =
+ MojoWaitMany(reinterpret_cast<const MojoHandle*>(&first_handle),
+ &first_signals, static_cast<uint32_t>(handles.size()),
+ deadline, &result_index, first_state);
+ return WaitManyResult(result, result_index);
+}
+
+// C++ 4.10, regarding pointer conversion, says that an integral null pointer
+// constant can be converted to |std::nullptr_t| (which is a typedef for
+// |decltype(nullptr)|). The opposite direction is not allowed.
+template <class HandleVectorType, class FlagsVectorType>
+inline WaitManyResult WaitMany(const HandleVectorType& handles,
+ const FlagsVectorType& signals,
+ MojoDeadline deadline,
+ decltype(nullptr) signals_states) {
+ if (signals.size() != handles.size())
+ return WaitManyResult(MOJO_RESULT_INVALID_ARGUMENT);
+ if (handles.size() >= kInvalidWaitManyIndexValue)
+ return WaitManyResult(MOJO_RESULT_RESOURCE_EXHAUSTED);
+
+ if (handles.size() == 0) {
+ return WaitManyResult(
+ MojoWaitMany(nullptr, nullptr, 0, deadline, nullptr, nullptr));
+ }
+
+ uint32_t result_index = kInvalidWaitManyIndexValue;
+ const Handle& first_handle = handles[0];
+ const MojoHandleSignals& first_signals = signals[0];
+ MojoResult result = MojoWaitMany(
+ reinterpret_cast<const MojoHandle*>(&first_handle), &first_signals,
+ static_cast<uint32_t>(handles.size()), deadline, &result_index, nullptr);
+ return WaitManyResult(result, result_index);
+}
+
+// |Close()| takes ownership of the handle, since it'll invalidate it.
+// Note: There's nothing to do, since the argument will be destroyed when it
+// goes out of scope.
+template <class HandleType>
+inline void Close(ScopedHandleBase<HandleType> /*handle*/) {
+}
+
+// Most users should typically use |Close()| (above) instead.
+inline MojoResult CloseRaw(Handle handle) {
+ return MojoClose(handle.value());
+}
+
+// Strict weak ordering, so that |Handle|s can be used as keys in |std::map|s,
+inline bool operator<(const Handle a, const Handle b) {
+ return a.value() < b.value();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_H_
diff --git a/mojo/public/cpp/system/macros.h b/mojo/public/cpp/system/macros.h
new file mode 100644
index 0000000..7f0fcd7
--- /dev/null
+++ b/mojo/public/cpp/system/macros.h
@@ -0,0 +1,81 @@
+// Copyright 2014 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.
+
+// Define a set of C++ specific macros.
+// Mojo C++ API users can assume that mojo/public/cpp/system/macros.h
+// includes mojo/public/c/system/macros.h.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
+
+#include "mojo/public/c/system/macros.h" // Symbols exposed.
+
+// A macro to disallow the copy constructor and operator= functions.
+#define MOJO_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+// Used to calculate the number of elements in an array.
+// (See |arraysize()| in Chromium's base/macros.h for more details.)
+namespace mojo {
+namespace internal {
+template <typename T, size_t N>
+char(&ArraySizeHelper(T(&array)[N]))[N];
+#if !defined(_MSC_VER)
+template <typename T, size_t N>
+char(&ArraySizeHelper(const T(&array)[N]))[N];
+#endif
+} // namespace internal
+} // namespace mojo
+#define MOJO_ARRAYSIZE(array) (sizeof(::mojo::internal::ArraySizeHelper(array)))
+
+// Used to make a type move-only. See Chromium's base/move.h for more
+// details. The MoveOnlyTypeForCPP03 typedef is for Chromium's base/callback.h
+// to tell that this type is move-only.
+#define MOJO_MOVE_ONLY_TYPE(type) \
+ private: \
+ type(type&); \
+ void operator=(type&); \
+ \
+ public: \
+ type&& Pass() MOJO_WARN_UNUSED_RESULT { return static_cast<type&&>(*this); } \
+ typedef void MoveOnlyTypeForCPP03; \
+ \
+ private:
+
+// The C++ standard requires that static const members have an out-of-class
+// definition (in a single compilation unit), but MSVC chokes on this (when
+// language extensions, which are required, are enabled). (You're only likely to
+// notice the need for a definition if you take the address of the member or,
+// more commonly, pass it to a function that takes it as a reference argument --
+// probably an STL function.) This macro makes MSVC do the right thing. See
+// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more
+// information. This workaround does not appear to be necessary after VS2015.
+// Use like:
+//
+// In the .h file:
+// struct Foo {
+// static const int kBar = 5;
+// };
+//
+// In the .cc file:
+// MOJO_STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar;
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define MOJO_STATIC_CONST_MEMBER_DEFINITION __declspec(selectany)
+#else
+#define MOJO_STATIC_CONST_MEMBER_DEFINITION
+#endif
+
+namespace mojo {
+
+// Used to explicitly mark the return value of a function as unused. (You this
+// if you are really sure you don't want to do anything with the return value of
+// a function marked with |MOJO_WARN_UNUSED_RESULT|.
+template <typename T>
+inline void ignore_result(const T&) {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
diff --git a/mojo/public/cpp/system/message_pipe.h b/mojo/public/cpp/system/message_pipe.h
new file mode 100644
index 0000000..e7a1e35
--- /dev/null
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -0,0 +1,119 @@
+// Copyright 2014 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.
+
+// This file provides a C++ wrapping around the Mojo C API for message pipes,
+// replacing the prefix of "Mojo" with a "mojo" namespace, and using more
+// strongly-typed representations of |MojoHandle|s.
+//
+// Please see "mojo/public/c/system/message_pipe.h" for complete documentation
+// of the API.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
+
+#include <assert.h>
+
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// A strongly-typed representation of a |MojoHandle| to one end of a message
+// pipe.
+class MessagePipeHandle : public Handle {
+ public:
+ MessagePipeHandle() {}
+ explicit MessagePipeHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+static_assert(sizeof(MessagePipeHandle) == sizeof(Handle),
+ "Bad size for C++ MessagePipeHandle");
+
+typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle;
+static_assert(sizeof(ScopedMessagePipeHandle) == sizeof(MessagePipeHandle),
+ "Bad size for C++ ScopedMessagePipeHandle");
+
+// Creates a message pipe. See |MojoCreateMessagePipe()| for complete
+// documentation.
+inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ ScopedMessagePipeHandle* message_pipe0,
+ ScopedMessagePipeHandle* message_pipe1) {
+ assert(message_pipe0);
+ assert(message_pipe1);
+ MessagePipeHandle handle0;
+ MessagePipeHandle handle1;
+ MojoResult rv = MojoCreateMessagePipe(
+ options, handle0.mutable_value(), handle1.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ message_pipe0->reset(handle0);
+ message_pipe1->reset(handle1);
+ return rv;
+}
+
+// The following "...Raw" versions fully expose the underlying API, and don't
+// help with ownership of handles (especially when writing messages). It is
+// expected that in most cases these methods will be called through generated
+// bindings anyway.
+// TODO(vtl): Write friendlier versions of these functions (using scoped
+// handles and/or vectors) if there is a demonstrated need for them.
+
+// Writes to a message pipe. If handles are attached, on success the handles
+// will no longer be valid (the receiver will receive equivalent, but logically
+// different, handles). See |MojoWriteMessage()| for complete documentation.
+inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ return MojoWriteMessage(
+ message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
+}
+
+// Reads from a message pipe. See |MojoReadMessage()| for complete
+// documentation.
+inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ return MojoReadMessage(
+ message_pipe.value(), bytes, num_bytes, handles, num_handles, flags);
+}
+
+// A wrapper class that automatically creates a message pipe and owns both
+// handles.
+class MessagePipe {
+ public:
+ MessagePipe();
+ explicit MessagePipe(const MojoCreateMessagePipeOptions& options);
+ ~MessagePipe();
+
+ ScopedMessagePipeHandle handle0;
+ ScopedMessagePipeHandle handle1;
+};
+
+inline MessagePipe::MessagePipe() {
+ MojoResult result = CreateMessagePipe(nullptr, &handle0, &handle1);
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) {
+ MojoResult result = CreateMessagePipe(&options, &handle0, &handle1);
+ MOJO_ALLOW_UNUSED_LOCAL(result);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::~MessagePipe() {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn
new file mode 100644
index 0000000..3b6058c
--- /dev/null
+++ b/mojo/public/cpp/system/tests/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2014 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.
+
+import("../../../mojo_sdk.gni")
+
+mojo_sdk_source_set("tests") {
+ testonly = true
+
+ sources = [
+ "core_unittest.cc",
+ "macros_unittest.cc",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/c/system/tests",
+ "mojo/public/cpp/environment:standalone",
+ "mojo/public/cpp/system",
+ "mojo/public/cpp/test_support:test_utils",
+ ]
+}
diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc
new file mode 100644
index 0000000..4dcad43
--- /dev/null
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -0,0 +1,495 @@
+// Copyright 2014 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.
+
+// This file tests the C++ Mojo system core wrappers.
+// TODO(vtl): Maybe rename "CoreCppTest" -> "CoreTest" if/when this gets
+// compiled into a different binary from the C API tests.
+
+#include "mojo/public/cpp/system/core.h"
+
+#include <stddef.h>
+
+#include <map>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const MojoHandleSignals kSignalReadableWritable =
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+
+const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE |
+ MOJO_HANDLE_SIGNAL_PEER_CLOSED;
+
+TEST(CoreCppTest, GetTimeTicksNow) {
+ const MojoTimeTicks start = GetTimeTicksNow();
+ EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+ << "GetTimeTicksNow should return nonzero value";
+}
+
+TEST(CoreCppTest, Basic) {
+ // Basic |Handle| implementation:
+ {
+ EXPECT_EQ(MOJO_HANDLE_INVALID, kInvalidHandleValue);
+
+ Handle h0;
+ EXPECT_EQ(kInvalidHandleValue, h0.value());
+ EXPECT_EQ(kInvalidHandleValue, *h0.mutable_value());
+ EXPECT_FALSE(h0.is_valid());
+
+ Handle h1(static_cast<MojoHandle>(123));
+ EXPECT_EQ(static_cast<MojoHandle>(123), h1.value());
+ EXPECT_EQ(static_cast<MojoHandle>(123), *h1.mutable_value());
+ EXPECT_TRUE(h1.is_valid());
+ *h1.mutable_value() = static_cast<MojoHandle>(456);
+ EXPECT_EQ(static_cast<MojoHandle>(456), h1.value());
+ EXPECT_TRUE(h1.is_valid());
+
+ h1.swap(h0);
+ EXPECT_EQ(static_cast<MojoHandle>(456), h0.value());
+ EXPECT_TRUE(h0.is_valid());
+ EXPECT_FALSE(h1.is_valid());
+
+ h1.set_value(static_cast<MojoHandle>(789));
+ h0.swap(h1);
+ EXPECT_EQ(static_cast<MojoHandle>(789), h0.value());
+ EXPECT_TRUE(h0.is_valid());
+ EXPECT_EQ(static_cast<MojoHandle>(456), h1.value());
+ EXPECT_TRUE(h1.is_valid());
+
+ // Make sure copy constructor works.
+ Handle h2(h0);
+ EXPECT_EQ(static_cast<MojoHandle>(789), h2.value());
+ // And assignment.
+ h2 = h1;
+ EXPECT_EQ(static_cast<MojoHandle>(456), h2.value());
+
+ // Make sure that we can put |Handle|s into |std::map|s.
+ h0 = Handle(static_cast<MojoHandle>(987));
+ h1 = Handle(static_cast<MojoHandle>(654));
+ h2 = Handle(static_cast<MojoHandle>(321));
+ Handle h3;
+ std::map<Handle, int> handle_to_int;
+ handle_to_int[h0] = 0;
+ handle_to_int[h1] = 1;
+ handle_to_int[h2] = 2;
+ handle_to_int[h3] = 3;
+
+ EXPECT_EQ(4u, handle_to_int.size());
+ EXPECT_FALSE(handle_to_int.find(h0) == handle_to_int.end());
+ EXPECT_EQ(0, handle_to_int[h0]);
+ EXPECT_FALSE(handle_to_int.find(h1) == handle_to_int.end());
+ EXPECT_EQ(1, handle_to_int[h1]);
+ EXPECT_FALSE(handle_to_int.find(h2) == handle_to_int.end());
+ EXPECT_EQ(2, handle_to_int[h2]);
+ EXPECT_FALSE(handle_to_int.find(h3) == handle_to_int.end());
+ EXPECT_EQ(3, handle_to_int[h3]);
+ EXPECT_TRUE(handle_to_int.find(Handle(static_cast<MojoHandle>(13579))) ==
+ handle_to_int.end());
+
+ // TODO(vtl): With C++11, support |std::unordered_map|s, etc. (Or figure out
+ // how to support the variations of |hash_map|.)
+ }
+
+ // |Handle|/|ScopedHandle| functions:
+ {
+ ScopedHandle h;
+
+ EXPECT_EQ(kInvalidHandleValue, h.get().value());
+
+ // This should be a no-op.
+ Close(h.Pass());
+
+ // It should still be invalid.
+ EXPECT_EQ(kInvalidHandleValue, h.get().value());
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ Wait(h.get(), ~MOJO_HANDLE_SIGNAL_NONE, 1000000, nullptr));
+
+ std::vector<Handle> wh;
+ wh.push_back(h.get());
+ std::vector<MojoHandleSignals> sigs;
+ sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE);
+ WaitManyResult wait_many_result =
+ WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, nullptr);
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result);
+ EXPECT_TRUE(wait_many_result.IsIndexValid());
+ EXPECT_FALSE(wait_many_result.AreSignalsStatesValid());
+
+ // Make sure that our specialized template correctly handles |NULL| as well
+ // as |nullptr|.
+ wait_many_result = WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, NULL);
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result);
+ EXPECT_EQ(0u, wait_many_result.index);
+ EXPECT_TRUE(wait_many_result.IsIndexValid());
+ EXPECT_FALSE(wait_many_result.AreSignalsStatesValid());
+ }
+
+ // |MakeScopedHandle| (just compilation tests):
+ {
+ EXPECT_FALSE(MakeScopedHandle(Handle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(MessagePipeHandle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(DataPipeProducerHandle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(DataPipeConsumerHandle()).is_valid());
+ EXPECT_FALSE(MakeScopedHandle(SharedBufferHandle()).is_valid());
+ }
+
+ // |MessagePipeHandle|/|ScopedMessagePipeHandle| functions:
+ {
+ MessagePipeHandle h_invalid;
+ EXPECT_FALSE(h_invalid.is_valid());
+ EXPECT_EQ(
+ MOJO_RESULT_INVALID_ARGUMENT,
+ WriteMessageRaw(
+ h_invalid, nullptr, 0, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+ char buffer[10] = {0};
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WriteMessageRaw(h_invalid,
+ buffer,
+ sizeof(buffer),
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ ReadMessageRaw(h_invalid,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ ReadMessageRaw(h_invalid,
+ buffer,
+ &buffer_size,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Basic tests of waiting and closing.
+ MojoHandle hv0 = kInvalidHandleValue;
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ EXPECT_FALSE(h0.get().is_valid());
+ EXPECT_FALSE(h1.get().is_valid());
+
+ CreateMessagePipe(nullptr, &h0, &h1);
+ EXPECT_TRUE(h0.get().is_valid());
+ EXPECT_TRUE(h1.get().is_valid());
+ EXPECT_NE(h0.get().value(), h1.get().value());
+ // Save the handle values, so we can check that things got closed
+ // correctly.
+ hv0 = h0.get().value();
+ MojoHandle hv1 = h1.get().value();
+ MojoHandleSignalsState state;
+
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
+
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ std::vector<Handle> wh;
+ wh.push_back(h0.get());
+ wh.push_back(h1.get());
+ std::vector<MojoHandleSignals> sigs;
+ sigs.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+ sigs.push_back(MOJO_HANDLE_SIGNAL_WRITABLE);
+ std::vector<MojoHandleSignalsState> states(sigs.size());
+ WaitManyResult wait_many_result = WaitMany(wh, sigs, 1000, &states);
+ EXPECT_EQ(MOJO_RESULT_OK, wait_many_result.result);
+ EXPECT_EQ(1u, wait_many_result.index);
+ EXPECT_TRUE(wait_many_result.IsIndexValid());
+ EXPECT_TRUE(wait_many_result.AreSignalsStatesValid());
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals);
+ EXPECT_EQ(kSignalAll, states[0].satisfiable_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[1].satisfied_signals);
+ EXPECT_EQ(kSignalAll, states[1].satisfiable_signals);
+
+ // Test closing |h1| explicitly.
+ Close(h1.Pass());
+ EXPECT_FALSE(h1.get().is_valid());
+
+ // Make sure |h1| is closed.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ Wait(Handle(hv1), ~MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_DEADLINE_INDEFINITE, nullptr));
+
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &state));
+
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
+ }
+ // |hv0| should have been closed when |h0| went out of scope, so this close
+ // should fail.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
+
+ // Actually test writing/reading messages.
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ CreateMessagePipe(nullptr, &h0, &h1);
+
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h0.get(),
+ kHello,
+ kHelloSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ MojoHandleSignalsState state;
+ EXPECT_EQ(MOJO_RESULT_OK, Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &state));
+ EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ char buffer[10] = {0};
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ReadMessageRaw(h1.get(),
+ buffer,
+ &buffer_size,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, buffer_size);
+ EXPECT_STREQ(kHello, buffer);
+
+ // Send a handle over the previously-establish message pipe. Use the
+ // |MessagePipe| wrapper (to test it), which automatically creates a
+ // message pipe.
+ MessagePipe mp;
+
+ // Write a message to |mp.handle0|, before we send |mp.handle1|.
+ const char kWorld[] = "world!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(mp.handle0.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Send |mp.handle1| over |h1| to |h0|.
+ MojoHandle handles[5];
+ handles[0] = mp.handle1.release().value();
+ EXPECT_NE(kInvalidHandleValue, handles[0]);
+ EXPECT_FALSE(mp.handle1.get().is_valid());
+ uint32_t handles_count = 1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h1.get(),
+ kHello,
+ kHelloSize,
+ handles,
+ handles_count,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // |handles[0]| should actually be invalid now.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handles[0]));
+
+ // Read "hello" and the sent handle.
+ EXPECT_EQ(MOJO_RESULT_OK, Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &state));
+ EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ memset(buffer, 0, sizeof(buffer));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(handles); i++)
+ handles[i] = kInvalidHandleValue;
+ handles_count = static_cast<uint32_t>(MOJO_ARRAYSIZE(handles));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ReadMessageRaw(h0.get(),
+ buffer,
+ &buffer_size,
+ handles,
+ &handles_count,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, buffer_size);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(1u, handles_count);
+ EXPECT_NE(kInvalidHandleValue, handles[0]);
+
+ // Read from the sent/received handle.
+ mp.handle1.reset(MessagePipeHandle(handles[0]));
+ // Save |handles[0]| to check that it gets properly closed.
+ hv0 = handles[0];
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(mp.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &state));
+ EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals);
+ EXPECT_EQ(kSignalAll, state.satisfiable_signals);
+
+ memset(buffer, 0, sizeof(buffer));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ for (size_t i = 0; i < MOJO_ARRAYSIZE(handles); i++)
+ handles[i] = kInvalidHandleValue;
+ handles_count = static_cast<uint32_t>(MOJO_ARRAYSIZE(handles));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ReadMessageRaw(mp.handle1.get(),
+ buffer,
+ &buffer_size,
+ handles,
+ &handles_count,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kWorldSize, buffer_size);
+ EXPECT_STREQ(kWorld, buffer);
+ EXPECT_EQ(0u, handles_count);
+ }
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0));
+ }
+
+ // TODO(vtl): Test |CloseRaw()|.
+ // TODO(vtl): Test |reset()| more thoroughly?
+}
+
+TEST(CoreCppTest, TearDownWithMessagesEnqueued) {
+ // Tear down a message pipe which still has a message enqueued, with the
+ // message also having a valid message pipe handle.
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ CreateMessagePipe(nullptr, &h0, &h1);
+
+ // Send a handle over the previously-establish message pipe.
+ ScopedMessagePipeHandle h2;
+ ScopedMessagePipeHandle h3;
+ CreateMessagePipe(nullptr, &h2, &h3);
+
+ // Write a message to |h2|, before we send |h3|.
+ const char kWorld[] = "world!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h2.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // And also a message to |h3|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h3.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Send |h3| over |h1| to |h0|.
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ MojoHandle h3_value;
+ h3_value = h3.release().value();
+ EXPECT_NE(kInvalidHandleValue, h3_value);
+ EXPECT_FALSE(h3.get().is_valid());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h1.get(),
+ kHello,
+ kHelloSize,
+ &h3_value,
+ 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // |h3_value| should actually be invalid now.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value()));
+ }
+
+ // Do this in a different order: make the enqueued message pipe handle only
+ // half-alive.
+ {
+ ScopedMessagePipeHandle h0;
+ ScopedMessagePipeHandle h1;
+ CreateMessagePipe(nullptr, &h0, &h1);
+
+ // Send a handle over the previously-establish message pipe.
+ ScopedMessagePipeHandle h2;
+ ScopedMessagePipeHandle h3;
+ CreateMessagePipe(nullptr, &h2, &h3);
+
+ // Write a message to |h2|, before we send |h3|.
+ const char kWorld[] = "world!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h2.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // And also a message to |h3|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h3.get(),
+ kWorld,
+ kWorldSize,
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Send |h3| over |h1| to |h0|.
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ MojoHandle h3_value;
+ h3_value = h3.release().value();
+ EXPECT_NE(kInvalidHandleValue, h3_value);
+ EXPECT_FALSE(h3.get().is_valid());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ WriteMessageRaw(h1.get(),
+ kHello,
+ kHelloSize,
+ &h3_value,
+ 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // |h3_value| should actually be invalid now.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value()));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value()));
+ }
+}
+
+TEST(CoreCppTest, ScopedHandleMoveCtor) {
+ ScopedSharedBufferHandle buffer1;
+ EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer1));
+ EXPECT_TRUE(buffer1.is_valid());
+
+ ScopedSharedBufferHandle buffer2;
+ EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer2));
+ EXPECT_TRUE(buffer2.is_valid());
+
+ // If this fails to close buffer1, ScopedHandleBase::CloseIfNecessary() will
+ // assert.
+ buffer1 = buffer2.Pass();
+
+ EXPECT_TRUE(buffer1.is_valid());
+ EXPECT_FALSE(buffer2.is_valid());
+}
+
+TEST(CoreCppTest, ScopedHandleMoveCtorSelf) {
+ ScopedSharedBufferHandle buffer1;
+ EXPECT_EQ(MOJO_RESULT_OK, CreateSharedBuffer(nullptr, 1024, &buffer1));
+ EXPECT_TRUE(buffer1.is_valid());
+
+ buffer1 = buffer1.Pass();
+
+ EXPECT_TRUE(buffer1.is_valid());
+}
+
+// TODO(vtl): Write data pipe tests.
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/system/tests/macros_unittest.cc b/mojo/public/cpp/system/tests/macros_unittest.cc
new file mode 100644
index 0000000..72a9b24
--- /dev/null
+++ b/mojo/public/cpp/system/tests/macros_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright 2014 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.
+
+// This file tests the C++ Mojo system macros and consists of "positive" tests,
+// i.e., those verifying that things work (without compile errors, or even
+// warnings if warnings are treated as errors).
+// TODO(vtl): Maybe rename "MacrosCppTest" -> "MacrosTest" if/when this gets
+// compiled into a different binary from the C API tests.
+// TODO(vtl): Fix no-compile tests (which are all disabled; crbug.com/105388)
+// and write some "negative" tests.
+
+#include "mojo/public/cpp/system/macros.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+// The test for |MOJO_STATIC_CONST_MEMBER_DEFINITION| is really a compile/link
+// test. To test it fully would really require a header file and multiple .cc
+// files, but we'll just cursorily verify it.
+//
+// This is defined outside of an anonymous namespace because
+// MOJO_STATIC_CONST_MEMBER_DEFINITION may not be used on internal symbols.
+struct StructWithStaticConstMember {
+ static const int kStaticConstMember = 123;
+};
+MOJO_STATIC_CONST_MEMBER_DEFINITION
+const int StructWithStaticConstMember::kStaticConstMember;
+
+namespace {
+
+// Note: MSVS is very strict (and arguably buggy) about warnings for classes
+// defined in a local scope, so define these globally.
+struct TestOverrideBaseClass {
+ virtual ~TestOverrideBaseClass() {}
+ virtual void ToBeOverridden() {}
+ virtual void AlsoToBeOverridden() = 0;
+};
+
+struct TestOverrideSubclass : public TestOverrideBaseClass {
+ ~TestOverrideSubclass() override {}
+ void ToBeOverridden() override {}
+ void AlsoToBeOverridden() override {}
+};
+
+TEST(MacrosCppTest, Override) {
+ TestOverrideSubclass x;
+ x.ToBeOverridden();
+ x.AlsoToBeOverridden();
+}
+
+// Note: MSVS is very strict (and arguably buggy) about warnings for classes
+// defined in a local scope, so define these globally.
+class TestDisallowCopyAndAssignClass {
+ public:
+ TestDisallowCopyAndAssignClass() {}
+ explicit TestDisallowCopyAndAssignClass(int) {}
+ void NoOp() {}
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestDisallowCopyAndAssignClass);
+};
+
+TEST(MacrosCppTest, DisallowCopyAndAssign) {
+ TestDisallowCopyAndAssignClass x;
+ x.NoOp();
+ TestDisallowCopyAndAssignClass y(789);
+ y.NoOp();
+}
+
+// Test that |MOJO_ARRAYSIZE()| works in a |static_assert()|.
+const int kGlobalArray[5] = {1, 2, 3, 4, 5};
+static_assert(MOJO_ARRAYSIZE(kGlobalArray) == 5u,
+ "MOJO_ARRAY_SIZE() failed in static_assert()");
+
+TEST(MacrosCppTest, ArraySize) {
+ double local_array[4] = {6.7, 7.8, 8.9, 9.0};
+ // MSVS considers this local variable unused since MOJO_ARRAYSIZE only takes
+ // the size of the type of the local and not the values itself.
+ MOJO_ALLOW_UNUSED_LOCAL(local_array);
+ EXPECT_EQ(4u, MOJO_ARRAYSIZE(local_array));
+}
+
+// Note: MSVS is very strict (and arguably buggy) about warnings for classes
+// defined in a local scope, so define these globally.
+class MoveOnlyInt {
+ MOJO_MOVE_ONLY_TYPE(MoveOnlyInt)
+
+ public:
+ MoveOnlyInt() : is_set_(false), value_() {}
+ explicit MoveOnlyInt(int value) : is_set_(true), value_(value) {}
+ ~MoveOnlyInt() {}
+
+ // Move-only constructor and operator=.
+ MoveOnlyInt(MoveOnlyInt&& other) { *this = other.Pass(); }
+ MoveOnlyInt& operator=(MoveOnlyInt&& other) {
+ if (&other != this) {
+ is_set_ = other.is_set_;
+ value_ = other.value_;
+ other.is_set_ = false;
+ }
+ return *this;
+ }
+
+ int value() const {
+ assert(is_set());
+ return value_;
+ }
+ bool is_set() const { return is_set_; }
+
+ private:
+ bool is_set_;
+ int value_;
+};
+
+TEST(MacrosCppTest, MoveOnlyType) {
+ MoveOnlyInt x(123);
+ EXPECT_TRUE(x.is_set());
+ EXPECT_EQ(123, x.value());
+ MoveOnlyInt y;
+ EXPECT_FALSE(y.is_set());
+ y = x.Pass();
+ EXPECT_FALSE(x.is_set());
+ EXPECT_TRUE(y.is_set());
+ EXPECT_EQ(123, y.value());
+ MoveOnlyInt z(y.Pass());
+ EXPECT_FALSE(y.is_set());
+ EXPECT_TRUE(z.is_set());
+ EXPECT_EQ(123, z.value());
+ z = z.Pass();
+ EXPECT_TRUE(z.is_set());
+ EXPECT_EQ(123, z.value());
+}
+
+// Use it, to make sure things get linked in and to avoid any warnings about
+// unused things.
+TEST(MacrosCppTest, StaticConstMemberDefinition) {
+ EXPECT_EQ(123, StructWithStaticConstMember::kStaticConstMember);
+}
+
+// The test for |ignore_result()| is also just a compilation test. (Note that
+// |MOJO_WARN_UNUSED_RESULT| can only be used in the prototype.
+int ReturnsIntYouMustUse() MOJO_WARN_UNUSED_RESULT;
+
+int ReturnsIntYouMustUse() {
+ return 123;
+}
+
+TEST(MacrosCppTest, IgnoreResult) {
+ ignore_result(ReturnsIntYouMustUse());
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
new file mode 100644
index 0000000..a1f7d31
--- /dev/null
+++ b/mojo/public/cpp/test_support/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2014 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.
+
+import("../../mojo_sdk.gni")
+
+# GYP version: mojo/public/mojo_public.gyp:mojo_public_test_utils
+mojo_sdk_source_set("test_utils") {
+ testonly = true
+
+ sources = [
+ "lib/test_support.cc",
+ "lib/test_utils.cc",
+ "test_utils.h",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/c/test_support",
+ "mojo/public/cpp/system",
+ ]
+}
diff --git a/mojo/public/cpp/test_support/lib/test_support.cc b/mojo/public/cpp/test_support/lib/test_support.cc
new file mode 100644
index 0000000..0b6035b
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/test_support.cc
@@ -0,0 +1,26 @@
+// Copyright 2014 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/test_support/test_support.h"
+
+#include <stdlib.h>
+
+namespace mojo {
+namespace test {
+
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+ const std::string& relative_path) {
+ char** names = MojoTestSupportEnumerateSourceRootRelativeDirectory(
+ relative_path.c_str());
+ std::vector<std::string> results;
+ for (char** ptr = names; *ptr != nullptr; ++ptr) {
+ results.push_back(*ptr);
+ free(*ptr);
+ }
+ free(names);
+ return results;
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/test_support/lib/test_utils.cc b/mojo/public/cpp/test_support/lib/test_utils.cc
new file mode 100644
index 0000000..210c6b1
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -0,0 +1,97 @@
+// Copyright 2013 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/test_support/test_utils.h"
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+bool WriteTextMessage(const MessagePipeHandle& handle,
+ const std::string& text) {
+ MojoResult rv = WriteMessageRaw(handle,
+ text.data(),
+ static_cast<uint32_t>(text.size()),
+ nullptr,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+ return rv == MOJO_RESULT_OK;
+}
+
+bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text) {
+ MojoResult rv;
+ bool did_wait = false;
+
+ uint32_t num_bytes = 0, num_handles = 0;
+ for (;;) {
+ rv = ReadMessageRaw(handle,
+ nullptr,
+ &num_bytes,
+ nullptr,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ if (did_wait) {
+ assert(false); // Looping endlessly!?
+ return false;
+ }
+ rv = Wait(handle, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
+ nullptr);
+ if (rv != MOJO_RESULT_OK)
+ return false;
+ did_wait = true;
+ } else {
+ assert(!num_handles);
+ break;
+ }
+ }
+
+ text->resize(num_bytes);
+ rv = ReadMessageRaw(handle,
+ &text->at(0),
+ &num_bytes,
+ nullptr,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ return rv == MOJO_RESULT_OK;
+}
+
+bool DiscardMessage(const MessagePipeHandle& handle) {
+ MojoResult rv = ReadMessageRaw(handle,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ return rv == MOJO_RESULT_OK;
+}
+
+void IterateAndReportPerf(const char* test_name,
+ const char* sub_test_name,
+ PerfTestSingleIteration single_iteration,
+ void* closure) {
+ // TODO(vtl): These should be specifiable using command-line flags.
+ static const size_t kGranularity = 100;
+ static const MojoTimeTicks kPerftestTimeMicroseconds = 3 * 1000000;
+
+ const MojoTimeTicks start_time = GetTimeTicksNow();
+ MojoTimeTicks end_time;
+ size_t iterations = 0;
+ do {
+ for (size_t i = 0; i < kGranularity; i++)
+ (*single_iteration)(closure);
+ iterations += kGranularity;
+
+ end_time = GetTimeTicksNow();
+ } while (end_time - start_time < kPerftestTimeMicroseconds);
+
+ MojoTestSupportLogPerfResult(test_name, sub_test_name,
+ 1000000.0 * iterations / (end_time - start_time),
+ "iterations/second");
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/test_support/test_support.h b/mojo/public/cpp/test_support/test_support.h
new file mode 100644
index 0000000..9a536e6
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_support.h
@@ -0,0 +1,35 @@
+// Copyright 2014 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_TEST_SUPPORT_TEST_SUPPORT_H_
+#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/c/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+inline void LogPerfResult(const char* test_name,
+ const char* sub_test_name,
+ double value,
+ const char* units) {
+ MojoTestSupportLogPerfResult(test_name, sub_test_name, value, units);
+}
+
+// Opens text file relative to the source root for reading.
+inline FILE* OpenSourceRootRelativeFile(const std::string& relative_path) {
+ return MojoTestSupportOpenSourceRootRelativeFile(relative_path.c_str());
+}
+
+// Returns the list of regular files in a directory relative to the source root.
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+ const std::string& relative_path);
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
diff --git a/mojo/public/cpp/test_support/test_utils.h b/mojo/public/cpp/test_support/test_utils.h
new file mode 100644
index 0000000..6fd5a9e
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_utils.h
@@ -0,0 +1,40 @@
+// Copyright 2013 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_TEST_SUPPORT_TEST_UTILS_H_
+#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace test {
+
+// Writes a message to |handle| with message data |text|. Returns true on
+// success.
+bool WriteTextMessage(const MessagePipeHandle& handle, const std::string& text);
+
+// Reads a message from |handle|, putting its contents into |*text|. Returns
+// true on success. (This blocks if necessary and will call |MojoReadMessage()|
+// multiple times, e.g., to query the size of the message.)
+bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text);
+
+// Discards a message from |handle|. Returns true on success. (This does not
+// block. It will fail if no message is available to discard.)
+bool DiscardMessage(const MessagePipeHandle& handle);
+
+// Run |single_iteration| an appropriate number of times and report its
+// performance appropriately. (This actually runs |single_iteration| for a fixed
+// amount of time and reports the number of iterations per unit time.)
+typedef void (*PerfTestSingleIteration)(void* closure);
+void IterateAndReportPerf(const char* test_name,
+ const char* sub_test_name,
+ PerfTestSingleIteration single_iteration,
+ void* closure);
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
diff --git a/mojo/public/cpp/utility/BUILD.gn b/mojo/public/cpp/utility/BUILD.gn
new file mode 100644
index 0000000..96c1d11
--- /dev/null
+++ b/mojo/public/cpp/utility/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2014 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.
+
+import("../../mojo_sdk.gni")
+
+mojo_sdk_source_set("utility") {
+ sources = [
+ "lib/mutex.cc",
+ "lib/run_loop.cc",
+ "lib/thread.cc",
+ "lib/thread_local.h",
+ "lib/thread_local_posix.cc",
+ "lib/thread_local_win.cc",
+ "mutex.h",
+ "run_loop.h",
+ "run_loop_handler.h",
+ "thread.h",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/cpp/bindings:callback",
+ "mojo/public/cpp/system",
+ ]
+
+ if (is_win) {
+ # See crbug.com/342893:
+ sources -= [
+ "lib/mutex.cc",
+ "lib/thread.cc",
+ "mutex.h",
+ "thread.h",
+ ]
+ }
+}
diff --git a/mojo/public/cpp/utility/lib/mutex.cc b/mojo/public/cpp/utility/lib/mutex.cc
new file mode 100644
index 0000000..23370e1
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/mutex.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 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/utility/mutex.h"
+
+#include <assert.h>
+#include <errno.h>
+
+namespace mojo {
+
+// Release builds have inlined (non-error-checking) definitions in the header.
+#if !defined(NDEBUG)
+Mutex::Mutex() {
+ pthread_mutexattr_t mutexattr;
+ int rv = pthread_mutexattr_init(&mutexattr);
+ assert(rv == 0);
+ rv = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
+ assert(rv == 0);
+ rv = pthread_mutex_init(&mutex_, &mutexattr);
+ assert(rv == 0);
+ rv = pthread_mutexattr_destroy(&mutexattr);
+ assert(rv == 0);
+}
+
+Mutex::~Mutex() {
+ int rv = pthread_mutex_destroy(&mutex_);
+ assert(rv == 0);
+}
+
+void Mutex::Lock() {
+ int rv = pthread_mutex_lock(&mutex_);
+ assert(rv == 0);
+}
+
+void Mutex::Unlock() {
+ int rv = pthread_mutex_unlock(&mutex_);
+ assert(rv == 0);
+}
+
+bool Mutex::TryLock() {
+ int rv = pthread_mutex_trylock(&mutex_);
+ assert(rv == 0 || rv == EBUSY);
+ return rv == 0;
+}
+
+void Mutex::AssertHeld() {
+ assert(pthread_mutex_lock(&mutex_) == EDEADLK);
+}
+#endif // !defined(NDEBUG)
+
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/run_loop.cc b/mojo/public/cpp/utility/lib/run_loop.cc
new file mode 100644
index 0000000..7faf748
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/run_loop.cc
@@ -0,0 +1,267 @@
+// Copyright 2014 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/utility/run_loop.h"
+
+#include <assert.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "mojo/public/cpp/utility/lib/thread_local.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+
+namespace mojo {
+namespace {
+
+internal::ThreadLocalPointer<RunLoop> current_run_loop;
+
+const MojoTimeTicks kInvalidTimeTicks = static_cast<MojoTimeTicks>(0);
+
+} // namespace
+
+// State needed for one iteration of WaitMany().
+struct RunLoop::WaitState {
+ WaitState() : deadline(MOJO_DEADLINE_INDEFINITE) {}
+
+ std::vector<Handle> handles;
+ std::vector<MojoHandleSignals> handle_signals;
+ MojoDeadline deadline;
+};
+
+struct RunLoop::RunState {
+ RunState() : should_quit(false) {}
+
+ bool should_quit;
+};
+
+RunLoop::RunLoop()
+ : run_state_(nullptr), next_handler_id_(0), next_sequence_number_(0) {
+ assert(!current());
+ current_run_loop.Set(this);
+}
+
+RunLoop::~RunLoop() {
+ assert(current() == this);
+ NotifyHandlers(MOJO_RESULT_ABORTED, IGNORE_DEADLINE);
+ current_run_loop.Set(nullptr);
+}
+
+// static
+void RunLoop::SetUp() {
+ current_run_loop.Allocate();
+}
+
+// static
+void RunLoop::TearDown() {
+ assert(!current());
+ current_run_loop.Free();
+}
+
+// static
+RunLoop* RunLoop::current() {
+ return current_run_loop.Get();
+}
+
+void RunLoop::AddHandler(RunLoopHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline) {
+ assert(current() == this);
+ assert(handler);
+ assert(handle.is_valid());
+ // Assume it's an error if someone tries to reregister an existing handle.
+ assert(0u == handler_data_.count(handle));
+ HandlerData handler_data;
+ handler_data.handler = handler;
+ handler_data.handle_signals = handle_signals;
+ handler_data.deadline =
+ (deadline == MOJO_DEADLINE_INDEFINITE)
+ ? kInvalidTimeTicks
+ : GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline);
+ handler_data.id = next_handler_id_++;
+ handler_data_[handle] = handler_data;
+}
+
+void RunLoop::RemoveHandler(const Handle& handle) {
+ assert(current() == this);
+ handler_data_.erase(handle);
+}
+
+bool RunLoop::HasHandler(const Handle& handle) const {
+ return handler_data_.find(handle) != handler_data_.end();
+}
+
+void RunLoop::Run() {
+ RunInternal(UNTIL_EMPTY);
+}
+
+void RunLoop::RunUntilIdle() {
+ RunInternal(UNTIL_IDLE);
+}
+
+void RunLoop::RunInternal(RunMode run_mode) {
+ assert(current() == this);
+ RunState* old_state = run_state_;
+ RunState run_state;
+ run_state_ = &run_state;
+ for (;;) {
+ bool did_work = DoDelayedWork();
+ if (run_state.should_quit)
+ break;
+ did_work |= Wait(run_mode == UNTIL_IDLE);
+ if (run_state.should_quit)
+ break;
+ if (!did_work && run_mode == UNTIL_IDLE)
+ break;
+ }
+ run_state_ = old_state;
+}
+
+bool RunLoop::DoDelayedWork() {
+ MojoTimeTicks now = GetTimeTicksNow();
+ if (!delayed_tasks_.empty() && delayed_tasks_.top().run_time <= now) {
+ PendingTask task = delayed_tasks_.top();
+ delayed_tasks_.pop();
+ task.task.Run();
+ return true;
+ }
+ return false;
+}
+
+void RunLoop::Quit() {
+ assert(current() == this);
+ if (run_state_)
+ run_state_->should_quit = true;
+}
+
+void RunLoop::PostDelayedTask(const Closure& task, MojoTimeTicks delay) {
+ assert(current() == this);
+ MojoTimeTicks run_time = delay + GetTimeTicksNow();
+ delayed_tasks_.push(PendingTask(task, run_time, next_sequence_number_++));
+}
+
+bool RunLoop::Wait(bool non_blocking) {
+ const WaitState wait_state = GetWaitState(non_blocking);
+ if (wait_state.handles.empty()) {
+ if (delayed_tasks_.empty())
+ Quit();
+ return false;
+ }
+
+ const WaitManyResult wmr =
+ WaitMany(wait_state.handles, wait_state.handle_signals,
+ wait_state.deadline, nullptr);
+
+ if (!wmr.IsIndexValid()) {
+ assert(wmr.result == MOJO_RESULT_DEADLINE_EXCEEDED);
+ return NotifyHandlers(MOJO_RESULT_DEADLINE_EXCEEDED, CHECK_DEADLINE);
+ }
+
+ Handle handle = wait_state.handles[wmr.index];
+ assert(handler_data_.find(handle) != handler_data_.end());
+ RunLoopHandler* handler = handler_data_[handle].handler;
+
+ switch (wmr.result) {
+ case MOJO_RESULT_OK:
+ handler->OnHandleReady(handle);
+ return true;
+ case MOJO_RESULT_INVALID_ARGUMENT:
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ // Remove the handle first, this way if OnHandleError() tries to remove
+ // the handle our iterator isn't invalidated.
+ handler_data_.erase(handle);
+ handler->OnHandleError(handle, wmr.result);
+ return true;
+ default:
+ assert(false);
+ return false;
+ }
+}
+
+bool RunLoop::NotifyHandlers(MojoResult error, CheckDeadline check) {
+ bool notified = false;
+
+ // Make a copy in case someone tries to add/remove new handlers as part of
+ // notifying.
+ const HandleToHandlerData cloned_handlers(handler_data_);
+ const MojoTimeTicks now(GetTimeTicksNow());
+ for (HandleToHandlerData::const_iterator i = cloned_handlers.begin();
+ i != cloned_handlers.end();
+ ++i) {
+ // Only check deadline exceeded if that's what we're notifying.
+ if (check == CHECK_DEADLINE &&
+ (i->second.deadline == kInvalidTimeTicks || i->second.deadline > now)) {
+ continue;
+ }
+
+ // Since we're iterating over a clone of the handlers, verify the handler
+ // is still valid before notifying.
+ if (handler_data_.find(i->first) == handler_data_.end() ||
+ handler_data_[i->first].id != i->second.id) {
+ continue;
+ }
+
+ RunLoopHandler* handler = i->second.handler;
+ handler_data_.erase(i->first);
+ handler->OnHandleError(i->first, error);
+ notified = true;
+ }
+
+ return notified;
+}
+
+RunLoop::WaitState RunLoop::GetWaitState(bool non_blocking) const {
+ WaitState wait_state;
+ MojoTimeTicks min_time = kInvalidTimeTicks;
+ for (HandleToHandlerData::const_iterator i = handler_data_.begin();
+ i != handler_data_.end();
+ ++i) {
+ wait_state.handles.push_back(i->first);
+ wait_state.handle_signals.push_back(i->second.handle_signals);
+ if (!non_blocking && i->second.deadline != kInvalidTimeTicks &&
+ (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) {
+ min_time = i->second.deadline;
+ }
+ }
+ if (!delayed_tasks_.empty()) {
+ MojoTimeTicks delayed_min_time = delayed_tasks_.top().run_time;
+ if (min_time == kInvalidTimeTicks)
+ min_time = delayed_min_time;
+ else
+ min_time = std::min(min_time, delayed_min_time);
+ }
+ if (non_blocking) {
+ wait_state.deadline = static_cast<MojoDeadline>(0);
+ } else if (min_time != kInvalidTimeTicks) {
+ const MojoTimeTicks now = GetTimeTicksNow();
+ if (min_time < now)
+ wait_state.deadline = static_cast<MojoDeadline>(0);
+ else
+ wait_state.deadline = static_cast<MojoDeadline>(min_time - now);
+ }
+ return wait_state;
+}
+
+RunLoop::PendingTask::PendingTask(const Closure& task,
+ MojoTimeTicks run_time,
+ uint64_t sequence_number)
+ : task(task), run_time(run_time), sequence_number(sequence_number) {
+}
+
+RunLoop::PendingTask::~PendingTask() {
+}
+
+bool RunLoop::PendingTask::operator<(const RunLoop::PendingTask& other) const {
+ if (run_time != other.run_time) {
+ // std::priority_queue<> puts the least element at the end of the queue. We
+ // want the soonest eligible task to be at the head of the queue, so
+ // run_times further in the future are considered lesser.
+ return run_time > other.run_time;
+ }
+
+ return sequence_number > other.sequence_number;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/thread.cc b/mojo/public/cpp/utility/lib/thread.cc
new file mode 100644
index 0000000..40f0bdd
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 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/utility/thread.h"
+
+#include <assert.h>
+
+namespace mojo {
+
+Thread::Thread() : options_(), thread_(), started_(false), joined_(false) {
+}
+
+Thread::Thread(const Options& options)
+ : options_(options), thread_(), started_(false), joined_(false) {
+}
+
+Thread::~Thread() {
+ // If it was started, it must have been joined.
+ assert(!started_ || joined_);
+}
+
+void Thread::Start() {
+ assert(!started_);
+ assert(!joined_);
+
+ pthread_attr_t attr;
+ int rv = pthread_attr_init(&attr);
+ MOJO_ALLOW_UNUSED_LOCAL(rv);
+ assert(rv == 0);
+
+ // Non-default stack size?
+ if (options_.stack_size() != 0) {
+ rv = pthread_attr_setstacksize(&attr, options_.stack_size());
+ assert(rv == 0);
+ }
+
+ started_ = true;
+ rv = pthread_create(&thread_, &attr, &ThreadRunTrampoline, this);
+ assert(rv == 0);
+
+ rv = pthread_attr_destroy(&attr);
+ assert(rv == 0);
+}
+
+void Thread::Join() {
+ // Must have been started but not yet joined.
+ assert(started_);
+ assert(!joined_);
+
+ joined_ = true;
+ int rv = pthread_join(thread_, nullptr);
+ MOJO_ALLOW_UNUSED_LOCAL(rv);
+ assert(rv == 0);
+}
+
+// static
+void* Thread::ThreadRunTrampoline(void* arg) {
+ Thread* self = static_cast<Thread*>(arg);
+ self->Run();
+ return nullptr;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/thread_local.h b/mojo/public/cpp/utility/lib/thread_local.h
new file mode 100644
index 0000000..f5461ee
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local.h
@@ -0,0 +1,54 @@
+// Copyright 2014 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_UTILITY_LIB_THREAD_LOCAL_H_
+#define MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
+
+#ifndef _WIN32
+#include <pthread.h>
+#endif
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Helper functions that abstract the cross-platform APIs.
+struct ThreadLocalPlatform {
+#ifdef _WIN32
+ typedef unsigned long SlotType;
+#else
+ typedef pthread_key_t SlotType;
+#endif
+
+ static void AllocateSlot(SlotType* slot);
+ static void FreeSlot(SlotType slot);
+ static void* GetValueFromSlot(SlotType slot);
+ static void SetValueInSlot(SlotType slot, void* value);
+};
+
+// This class is intended to be statically allocated.
+template <typename P>
+class ThreadLocalPointer {
+ public:
+ ThreadLocalPointer() : slot_() {}
+
+ void Allocate() { ThreadLocalPlatform::AllocateSlot(&slot_); }
+
+ void Free() { ThreadLocalPlatform::FreeSlot(slot_); }
+
+ P* Get() {
+ return static_cast<P*>(ThreadLocalPlatform::GetValueFromSlot(slot_));
+ }
+
+ void Set(P* value) { ThreadLocalPlatform::SetValueInSlot(slot_, value); }
+
+ private:
+ ThreadLocalPlatform::SlotType slot_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
diff --git a/mojo/public/cpp/utility/lib/thread_local_posix.cc b/mojo/public/cpp/utility/lib/thread_local_posix.cc
new file mode 100644
index 0000000..ea7343e
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local_posix.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 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/utility/lib/thread_local.h"
+
+#include <assert.h>
+
+namespace mojo {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ if (pthread_key_create(slot, nullptr) != 0) {
+ assert(false);
+ }
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+ if (pthread_key_delete(slot) != 0) {
+ assert(false);
+ }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+ return pthread_getspecific(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+ if (pthread_setspecific(slot, value) != 0) {
+ assert(false);
+ }
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/lib/thread_local_win.cc b/mojo/public/cpp/utility/lib/thread_local_win.cc
new file mode 100644
index 0000000..b8239cb
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local_win.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 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/utility/lib/thread_local.h"
+
+#include <windows.h>
+#include <assert.h>
+
+namespace mojo {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ *slot = TlsAlloc();
+ assert(*slot != TLS_OUT_OF_INDEXES);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+ if (!TlsFree(slot)) {
+ assert(false);
+ }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+ return TlsGetValue(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+ if (!TlsSetValue(slot, value)) {
+ assert(false);
+ }
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/mutex.h b/mojo/public/cpp/utility/mutex.h
new file mode 100644
index 0000000..4dc4aee
--- /dev/null
+++ b/mojo/public/cpp/utility/mutex.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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_UTILITY_MUTEX_H_
+#define MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
+
+#ifdef _WIN32
+#error "Not implemented: See crbug.com/342893."
+#endif
+
+#include <pthread.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+#ifdef NDEBUG
+// Note: Make a C++ constant for |PTHREAD_MUTEX_INITIALIZER|. (We can't directly
+// use the C macro in an initializer list, since it might expand to |{ ... }|.)
+namespace internal {
+const pthread_mutex_t kPthreadMutexInitializer = PTHREAD_MUTEX_INITIALIZER;
+}
+#endif
+
+class Mutex {
+ public:
+#ifdef NDEBUG
+ Mutex() : mutex_(internal::kPthreadMutexInitializer) {}
+ ~Mutex() { pthread_mutex_destroy(&mutex_); }
+
+ void Lock() { pthread_mutex_lock(&mutex_); }
+ void Unlock() { pthread_mutex_unlock(&mutex_); }
+ bool TryLock() { return pthread_mutex_trylock(&mutex_) == 0; }
+
+ void AssertHeld() {}
+#else
+ Mutex();
+ ~Mutex();
+
+ void Lock();
+ void Unlock();
+ bool TryLock();
+
+ void AssertHeld();
+#endif
+
+ private:
+ pthread_mutex_t mutex_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Mutex);
+};
+
+class MutexLock {
+ public:
+ explicit MutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); }
+ ~MutexLock() { mutex_->Unlock(); }
+
+ private:
+ Mutex* const mutex_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLock);
+};
+
+// Catch bug where variable name is omitted (e.g., |MutexLock (&mu)|).
+#define MutexLock(x) static_assert(0, "MutexLock() missing variable name");
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
diff --git a/mojo/public/cpp/utility/run_loop.h b/mojo/public/cpp/utility/run_loop.h
new file mode 100644
index 0000000..4673eaa
--- /dev/null
+++ b/mojo/public/cpp/utility/run_loop.h
@@ -0,0 +1,156 @@
+// Copyright 2013 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_UTILITY_RUN_LOOP_H_
+#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
+
+#include <map>
+#include <queue>
+
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class RunLoopHandler;
+
+// Watches handles for signals and calls event handlers when they occur. Also
+// executes delayed tasks. This class should only be used by a single thread.
+class RunLoop {
+ public:
+ RunLoop();
+ ~RunLoop();
+
+ // Sets up state needed for RunLoop. This must be invoked before creating a
+ // RunLoop.
+ static void SetUp();
+
+ // Cleans state created by Setup().
+ static void TearDown();
+
+ // Returns the RunLoop for the current thread. Returns null if not yet
+ // created.
+ static RunLoop* current();
+
+ // Registers a RunLoopHandler for the specified handle. It is an error to
+ // register more than one handler for a handle, and crashes the process.
+ //
+ // The handler's OnHandleReady() method is invoked after one of the signals in
+ // |handle_signals| occurs. Note that the handler remains registered until
+ // explicitly removed or an error occurs.
+ //
+ // The handler's OnHandleError() method is invoked if the deadline elapses, an
+ // error is detected, or the RunLoop is being destroyed. The handler is
+ // automatically unregistered before calling OnHandleError(), so it will not
+ // receive any further notifications.
+ void AddHandler(RunLoopHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline);
+ void RemoveHandler(const Handle& handle);
+ bool HasHandler(const Handle& handle) const;
+
+ // Runs the loop servicing handles and tasks as they are ready. This returns
+ // when Quit() is invoked, or there are no more handles nor tasks.
+ void Run();
+
+ // Runs the loop servicing any handles and tasks that are ready. Does not wait
+ // for handles or tasks to become ready before returning. Returns early if
+ // Quit() is invoked.
+ void RunUntilIdle();
+
+ void Quit();
+
+ // Adds a task to be performed after delay has elapsed. Must be posted to the
+ // current thread's RunLoop.
+ void PostDelayedTask(const Closure& task, MojoTimeTicks delay);
+
+ private:
+ struct RunState;
+ struct WaitState;
+
+ // Contains the data needed to track a request to AddHandler().
+ struct HandlerData {
+ HandlerData()
+ : handler(nullptr),
+ handle_signals(MOJO_HANDLE_SIGNAL_NONE),
+ deadline(0),
+ id(0) {}
+
+ RunLoopHandler* handler;
+ MojoHandleSignals handle_signals;
+ MojoTimeTicks deadline;
+ // See description of |RunLoop::next_handler_id_| for details.
+ int id;
+ };
+
+ typedef std::map<Handle, HandlerData> HandleToHandlerData;
+
+ // Used for NotifyHandlers to specify whether HandlerData's |deadline|
+ // should be checked prior to notifying.
+ enum CheckDeadline { CHECK_DEADLINE, IGNORE_DEADLINE };
+
+ // Mode of operation of the run loop.
+ enum RunMode { UNTIL_EMPTY, UNTIL_IDLE };
+
+ // Runs the loop servicing any handles and tasks that are ready. If
+ // |run_mode| is |UNTIL_IDLE|, does not wait for handles or tasks to become
+ // ready before returning. Returns early if Quit() is invoked.
+ void RunInternal(RunMode run_mode);
+
+ // Do one unit of delayed work, if eligible. Returns true is a task was run.
+ bool DoDelayedWork();
+
+ // Waits for a handle to be ready or until the next task must be run. Returns
+ // after servicing at least one handle (or there are no more handles) unless
+ // a task must be run or |non_blocking| is true, in which case it will also
+ // return if no task is registered and servicing at least one handle would
+ // require blocking. Returns true if a RunLoopHandler was notified.
+ bool Wait(bool non_blocking);
+
+ // Notifies handlers of |error|. If |check| == CHECK_DEADLINE, this will
+ // only notify handlers whose deadline has expired and skips the rest.
+ // Returns true if a RunLoopHandler was notified.
+ bool NotifyHandlers(MojoResult error, CheckDeadline check);
+
+ // Returns the state needed to pass to WaitMany().
+ WaitState GetWaitState(bool non_blocking) const;
+
+ HandleToHandlerData handler_data_;
+
+ // If non-null we're running (inside Run()). Member references a value on the
+ // stack.
+ RunState* run_state_;
+
+ // An ever increasing value assigned to each HandlerData::id. Used to detect
+ // uniqueness while notifying. That is, while notifying expired timers we copy
+ // |handler_data_| and only notify handlers whose id match. If the id does not
+ // match it means the handler was removed then added so that we shouldn't
+ // notify it.
+ int next_handler_id_;
+
+ struct PendingTask {
+ PendingTask(const Closure& task,
+ MojoTimeTicks runtime,
+ uint64_t sequence_number);
+ ~PendingTask();
+
+ bool operator<(const PendingTask& other) const;
+
+ Closure task;
+ MojoTimeTicks run_time;
+ uint64_t sequence_number;
+ };
+ // An ever increasing sequence number attached to each pending task in order
+ // to preserve relative order of tasks posted at the 'same' time.
+ uint64_t next_sequence_number_;
+ typedef std::priority_queue<PendingTask> DelayedTaskQueue;
+ DelayedTaskQueue delayed_tasks_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoop);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
diff --git a/mojo/public/cpp/utility/run_loop_handler.h b/mojo/public/cpp/utility/run_loop_handler.h
new file mode 100644
index 0000000..69838d5
--- /dev/null
+++ b/mojo/public/cpp/utility/run_loop_handler.h
@@ -0,0 +1,25 @@
+// Copyright 2013 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_UTILITY_RUN_LOOP_HANDLER_H_
+#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// Used by RunLoop to notify when a handle is either ready or has become
+// invalid.
+class RunLoopHandler {
+ public:
+ virtual void OnHandleReady(const Handle& handle) = 0;
+ virtual void OnHandleError(const Handle& handle, MojoResult result) = 0;
+
+ protected:
+ virtual ~RunLoopHandler() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
diff --git a/mojo/public/cpp/utility/tests/BUILD.gn b/mojo/public/cpp/utility/tests/BUILD.gn
new file mode 100644
index 0000000..acbbc9f
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2014 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.
+
+import("../../../mojo_sdk.gni")
+
+mojo_sdk_source_set("tests") {
+ testonly = true
+
+ sources = [
+ "run_loop_unittest.cc",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ mojo_sdk_deps = [
+ "mojo/public/cpp/environment:standalone",
+ "mojo/public/cpp/system",
+ "mojo/public/cpp/test_support:test_utils",
+ "mojo/public/cpp/utility",
+ ]
+
+ # crbug.com/342893
+ if (!is_win) {
+ sources += [
+ "mutex_unittest.cc",
+ "thread_unittest.cc",
+ ]
+ }
+}
diff --git a/mojo/public/cpp/utility/tests/mutex_unittest.cc b/mojo/public/cpp/utility/tests/mutex_unittest.cc
new file mode 100644
index 0000000..78e95c5
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/mutex_unittest.cc
@@ -0,0 +1,259 @@
+// Copyright 2014 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/utility/mutex.h"
+
+#include <stdlib.h> // For |rand()|.
+#include <time.h> // For |nanosleep()| (defined by POSIX).
+
+#include <vector>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(MutexTest, TrivialSingleThreaded) {
+ Mutex mutex;
+
+ mutex.Lock();
+ mutex.AssertHeld();
+ mutex.Unlock();
+
+ EXPECT_TRUE(mutex.TryLock());
+ mutex.AssertHeld();
+ mutex.Unlock();
+
+ {
+ MutexLock lock(&mutex);
+ mutex.AssertHeld();
+ }
+
+ EXPECT_TRUE(mutex.TryLock());
+ mutex.Unlock();
+}
+
+class Fiddler {
+ public:
+ enum Type { kTypeLock, kTypeTry };
+ Fiddler(size_t times_to_lock,
+ Type type,
+ bool should_sleep,
+ Mutex* mutex,
+ int* shared_value)
+ : times_to_lock_(times_to_lock),
+ type_(type),
+ should_sleep_(should_sleep),
+ mutex_(mutex),
+ shared_value_(shared_value) {
+ }
+
+ ~Fiddler() {
+ }
+
+ void Fiddle() {
+ for (size_t i = 0; i < times_to_lock_;) {
+ switch (type_) {
+ case kTypeLock: {
+ mutex_->Lock();
+ int old_shared_value = *shared_value_;
+ if (should_sleep_)
+ SleepALittle();
+ *shared_value_ = old_shared_value + 1;
+ mutex_->Unlock();
+ i++;
+ break;
+ }
+ case kTypeTry:
+ if (mutex_->TryLock()) {
+ int old_shared_value = *shared_value_;
+ if (should_sleep_)
+ SleepALittle();
+ *shared_value_ = old_shared_value + 1;
+ mutex_->Unlock();
+ i++;
+ } else {
+ SleepALittle(); // Don't spin.
+ }
+ break;
+ }
+ }
+ }
+
+ private:
+ static void SleepALittle() {
+ static const long kNanosPerMilli = 1000000;
+ struct timespec req = {
+ 0, // Seconds.
+ (rand() % 10) * kNanosPerMilli // Nanoseconds.
+ };
+ int rv = nanosleep(&req, nullptr);
+ MOJO_ALLOW_UNUSED_LOCAL(rv);
+ assert(rv == 0);
+ }
+
+ const size_t times_to_lock_;
+ const Type type_;
+ const bool should_sleep_;
+ Mutex* const mutex_;
+ int* const shared_value_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler);
+};
+
+class FiddlerThread : public Thread {
+ public:
+ // Takes ownership of |fiddler|.
+ FiddlerThread(Fiddler* fiddler)
+ : fiddler_(fiddler) {
+ }
+
+ ~FiddlerThread() override { delete fiddler_; }
+
+ void Run() override { fiddler_->Fiddle(); }
+
+ private:
+ Fiddler* const fiddler_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread);
+};
+
+// This does a stress test (that also checks exclusion).
+TEST(MutexTest, ThreadedStress) {
+ static const size_t kNumThreads = 20;
+ static const int kTimesToLockEach = 20;
+ assert(kNumThreads % 4 == 0);
+
+ Mutex mutex;
+ int shared_value = 0;
+
+ std::vector<FiddlerThread*> fiddler_threads;
+
+ for (size_t i = 0; i < kNumThreads; i += 4) {
+ fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+ kTimesToLockEach, Fiddler::kTypeLock, false, &mutex, &shared_value)));
+ fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+ kTimesToLockEach, Fiddler::kTypeTry, false, &mutex, &shared_value)));
+ fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+ kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)));
+ fiddler_threads.push_back(new FiddlerThread(new Fiddler(
+ kTimesToLockEach, Fiddler::kTypeTry, true, &mutex, &shared_value)));
+ }
+
+ for (size_t i = 0; i < kNumThreads; i++)
+ fiddler_threads[i]->Start();
+
+ // Do some fiddling ourselves.
+ Fiddler(kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)
+ .Fiddle();
+
+ // Join.
+ for (size_t i = 0; i < kNumThreads; i++)
+ fiddler_threads[i]->Join();
+
+ EXPECT_EQ(static_cast<int>(kNumThreads + 1) * kTimesToLockEach, shared_value);
+
+ // Delete.
+ for (size_t i = 0; i < kNumThreads; i++)
+ delete fiddler_threads[i];
+ fiddler_threads.clear();
+}
+
+class TryThread : public Thread {
+ public:
+ explicit TryThread(Mutex* mutex) : mutex_(mutex), try_lock_succeeded_() {}
+ ~TryThread() override {}
+
+ void Run() override {
+ try_lock_succeeded_ = mutex_->TryLock();
+ if (try_lock_succeeded_)
+ mutex_->Unlock();
+ }
+
+ bool try_lock_succeeded() const { return try_lock_succeeded_; }
+
+ private:
+ Mutex* const mutex_;
+ bool try_lock_succeeded_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread);
+};
+
+TEST(MutexTest, TryLock) {
+ Mutex mutex;
+
+ // |TryLock()| should succeed -- we don't have the lock.
+ {
+ TryThread thread(&mutex);
+ thread.Start();
+ thread.Join();
+ EXPECT_TRUE(thread.try_lock_succeeded());
+ }
+
+ // Take the lock.
+ ASSERT_TRUE(mutex.TryLock());
+
+ // Now it should fail.
+ {
+ TryThread thread(&mutex);
+ thread.Start();
+ thread.Join();
+ EXPECT_FALSE(thread.try_lock_succeeded());
+ }
+
+ // Release the lock.
+ mutex.Unlock();
+
+ // It should succeed again.
+ {
+ TryThread thread(&mutex);
+ thread.Start();
+ thread.Join();
+ EXPECT_TRUE(thread.try_lock_succeeded());
+ }
+}
+
+
+// Tests of assertions for Debug builds.
+#if !defined(NDEBUG)
+// Test |AssertHeld()| (which is an actual user API).
+TEST(MutexTest, DebugAssertHeldFailure) {
+ Mutex mutex;
+ EXPECT_DEATH_IF_SUPPORTED(mutex.AssertHeld(), "");
+}
+
+// Test other consistency checks.
+TEST(MutexTest, DebugAssertionFailures) {
+ // Unlock without lock held.
+ EXPECT_DEATH_IF_SUPPORTED({
+ Mutex mutex;
+ mutex.Unlock();
+ }, "");
+
+ // Lock with lock held (on same thread).
+ EXPECT_DEATH_IF_SUPPORTED({
+ Mutex mutex;
+ mutex.Lock();
+ mutex.Lock();
+ }, "");
+
+ // Try lock with lock held.
+ EXPECT_DEATH_IF_SUPPORTED({
+ Mutex mutex;
+ mutex.Lock();
+ mutex.TryLock();
+ }, "");
+
+ // Destroy lock with lock held.
+ EXPECT_DEATH_IF_SUPPORTED({
+ Mutex mutex;
+ mutex.Lock();
+ }, "");
+}
+#endif // !defined(NDEBUG)
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/tests/run_loop_unittest.cc b/mojo/public/cpp/utility/tests/run_loop_unittest.cc
new file mode 100644
index 0000000..4ab4876
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/run_loop_unittest.cc
@@ -0,0 +1,425 @@
+// Copyright 2014 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/utility/run_loop.h"
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class TestRunLoopHandler : public RunLoopHandler {
+ public:
+ TestRunLoopHandler()
+ : ready_count_(0),
+ error_count_(0),
+ last_error_result_(MOJO_RESULT_OK) {
+ }
+ ~TestRunLoopHandler() override {}
+
+ void clear_ready_count() { ready_count_ = 0; }
+ int ready_count() const { return ready_count_; }
+
+ void clear_error_count() { error_count_ = 0; }
+ int error_count() const { return error_count_; }
+
+ MojoResult last_error_result() const { return last_error_result_; }
+
+ // RunLoopHandler:
+ void OnHandleReady(const Handle& handle) override { ready_count_++; }
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ error_count_++;
+ last_error_result_ = result;
+ }
+
+ private:
+ int ready_count_;
+ int error_count_;
+ MojoResult last_error_result_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestRunLoopHandler);
+};
+
+class RunLoopTest : public testing::Test {
+ public:
+ RunLoopTest() {}
+
+ void SetUp() override {
+ Test::SetUp();
+ RunLoop::SetUp();
+ }
+ void TearDown() override {
+ RunLoop::TearDown();
+ Test::TearDown();
+ }
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopTest);
+};
+
+// Trivial test to verify Run() with no added handles returns.
+TEST_F(RunLoopTest, ExitsWithNoHandles) {
+ RunLoop run_loop;
+ run_loop.Run();
+}
+
+class RemoveOnReadyRunLoopHandler : public TestRunLoopHandler {
+ public:
+ RemoveOnReadyRunLoopHandler() : run_loop_(nullptr) {}
+ ~RemoveOnReadyRunLoopHandler() override {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ void OnHandleReady(const Handle& handle) override {
+ run_loop_->RemoveHandler(handle);
+ TestRunLoopHandler::OnHandleReady(handle);
+ }
+
+ private:
+ RunLoop* run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveOnReadyRunLoopHandler);
+};
+
+// Verifies RunLoop quits when no more handles (handle is removed when ready).
+TEST_F(RunLoopTest, HandleReady) {
+ RemoveOnReadyRunLoopHandler handler;
+ MessagePipe test_pipe;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+ RunLoop run_loop;
+ handler.set_run_loop(&run_loop);
+ run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+ run_loop.Run();
+ EXPECT_EQ(1, handler.ready_count());
+ EXPECT_EQ(0, handler.error_count());
+ EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get()));
+}
+
+class QuitOnReadyRunLoopHandler : public TestRunLoopHandler {
+ public:
+ QuitOnReadyRunLoopHandler() : run_loop_(nullptr) {}
+ ~QuitOnReadyRunLoopHandler() override {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ void OnHandleReady(const Handle& handle) override {
+ run_loop_->Quit();
+ TestRunLoopHandler::OnHandleReady(handle);
+ }
+
+ private:
+ RunLoop* run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnReadyRunLoopHandler);
+};
+
+// Verifies Quit() from OnHandleReady() quits the loop.
+TEST_F(RunLoopTest, QuitFromReady) {
+ QuitOnReadyRunLoopHandler handler;
+ MessagePipe test_pipe;
+ EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string()));
+
+ RunLoop run_loop;
+ handler.set_run_loop(&run_loop);
+ run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+ run_loop.Run();
+ EXPECT_EQ(1, handler.ready_count());
+ EXPECT_EQ(0, handler.error_count());
+ EXPECT_TRUE(run_loop.HasHandler(test_pipe.handle0.get()));
+}
+
+class QuitOnErrorRunLoopHandler : public TestRunLoopHandler {
+ public:
+ QuitOnErrorRunLoopHandler() : run_loop_(nullptr) {}
+ ~QuitOnErrorRunLoopHandler() override {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ run_loop_->Quit();
+ TestRunLoopHandler::OnHandleError(handle, result);
+ }
+
+ private:
+ RunLoop* run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnErrorRunLoopHandler);
+};
+
+// Verifies Quit() when the deadline is reached works.
+TEST_F(RunLoopTest, QuitWhenDeadlineExpired) {
+ QuitOnErrorRunLoopHandler handler;
+ MessagePipe test_pipe;
+ RunLoop run_loop;
+ handler.set_run_loop(&run_loop);
+ run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ static_cast<MojoDeadline>(10000));
+ run_loop.Run();
+ EXPECT_EQ(0, handler.ready_count());
+ EXPECT_EQ(1, handler.error_count());
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, handler.last_error_result());
+ EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get()));
+}
+
+// Test that handlers are notified of loop destruction.
+TEST_F(RunLoopTest, Destruction) {
+ TestRunLoopHandler handler;
+ MessagePipe test_pipe;
+ {
+ RunLoop run_loop;
+ run_loop.AddHandler(&handler,
+ test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ }
+ EXPECT_EQ(1, handler.error_count());
+ EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result());
+}
+
+class RemoveManyRunLoopHandler : public TestRunLoopHandler {
+ public:
+ RemoveManyRunLoopHandler() : run_loop_(nullptr) {}
+ ~RemoveManyRunLoopHandler() override {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+ void add_handle(const Handle& handle) { handles_.push_back(handle); }
+
+ // RunLoopHandler:
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ for (size_t i = 0; i < handles_.size(); i++)
+ run_loop_->RemoveHandler(handles_[i]);
+ TestRunLoopHandler::OnHandleError(handle, result);
+ }
+
+ private:
+ std::vector<Handle> handles_;
+ RunLoop* run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveManyRunLoopHandler);
+};
+
+// Test that handlers are notified of loop destruction.
+TEST_F(RunLoopTest, MultipleHandleDestruction) {
+ RemoveManyRunLoopHandler odd_handler;
+ TestRunLoopHandler even_handler;
+ MessagePipe test_pipe1, test_pipe2, test_pipe3;
+ {
+ RunLoop run_loop;
+ odd_handler.set_run_loop(&run_loop);
+ odd_handler.add_handle(test_pipe1.handle0.get());
+ odd_handler.add_handle(test_pipe3.handle0.get());
+ run_loop.AddHandler(&odd_handler,
+ test_pipe1.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ run_loop.AddHandler(&even_handler,
+ test_pipe2.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ run_loop.AddHandler(&odd_handler,
+ test_pipe3.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ }
+ EXPECT_EQ(1, odd_handler.error_count());
+ EXPECT_EQ(1, even_handler.error_count());
+ EXPECT_EQ(MOJO_RESULT_ABORTED, odd_handler.last_error_result());
+ EXPECT_EQ(MOJO_RESULT_ABORTED, even_handler.last_error_result());
+}
+
+class AddHandlerOnErrorHandler : public TestRunLoopHandler {
+ public:
+ AddHandlerOnErrorHandler() : run_loop_(nullptr) {}
+ ~AddHandlerOnErrorHandler() override {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ run_loop_->AddHandler(this, handle,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ TestRunLoopHandler::OnHandleError(handle, result);
+ }
+
+ private:
+ RunLoop* run_loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(AddHandlerOnErrorHandler);
+};
+
+TEST_F(RunLoopTest, AddHandlerOnError) {
+ AddHandlerOnErrorHandler handler;
+ MessagePipe test_pipe;
+ {
+ RunLoop run_loop;
+ handler.set_run_loop(&run_loop);
+ run_loop.AddHandler(&handler,
+ test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ }
+ EXPECT_EQ(1, handler.error_count());
+ EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result());
+}
+
+TEST_F(RunLoopTest, Current) {
+ EXPECT_TRUE(RunLoop::current() == nullptr);
+ {
+ RunLoop run_loop;
+ EXPECT_EQ(&run_loop, RunLoop::current());
+ }
+ EXPECT_TRUE(RunLoop::current() == nullptr);
+}
+
+class NestingRunLoopHandler : public TestRunLoopHandler {
+ public:
+ static const size_t kDepthLimit;
+ static const char kSignalMagic;
+
+ NestingRunLoopHandler()
+ : run_loop_(nullptr),
+ pipe_(nullptr),
+ depth_(0),
+ reached_depth_limit_(false) {}
+
+ ~NestingRunLoopHandler() override {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+ void set_pipe(MessagePipe* pipe) { pipe_ = pipe; }
+ bool reached_depth_limit() const { return reached_depth_limit_; }
+
+ // RunLoopHandler:
+ void OnHandleReady(const Handle& handle) override {
+ TestRunLoopHandler::OnHandleReady(handle);
+ EXPECT_EQ(handle.value(), pipe_->handle0.get().value());
+
+ ReadSignal();
+ size_t current_depth = ++depth_;
+ if (current_depth < kDepthLimit) {
+ WriteSignal();
+ run_loop_->Run();
+ if (current_depth == kDepthLimit - 1) {
+ // The topmost loop Quit()-ed, so its parent takes back the
+ // control without exeeding deadline.
+ EXPECT_EQ(error_count(), 0);
+ } else {
+ EXPECT_EQ(error_count(), 1);
+ }
+
+ } else {
+ EXPECT_EQ(current_depth, kDepthLimit);
+ reached_depth_limit_ = true;
+ run_loop_->Quit();
+ }
+ --depth_;
+ }
+
+ void WriteSignal() {
+ char write_byte = kSignalMagic;
+ MojoResult write_result =
+ WriteMessageRaw(pipe_->handle1.get(), &write_byte, 1, nullptr, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+ EXPECT_EQ(write_result, MOJO_RESULT_OK);
+ }
+
+ void ReadSignal() {
+ char read_byte = 0;
+ uint32_t bytes_read = 1;
+ uint32_t handles_read = 0;
+ MojoResult read_result =
+ ReadMessageRaw(pipe_->handle0.get(), &read_byte, &bytes_read, nullptr,
+ &handles_read, MOJO_READ_MESSAGE_FLAG_NONE);
+ EXPECT_EQ(read_result, MOJO_RESULT_OK);
+ EXPECT_EQ(read_byte, kSignalMagic);
+ }
+
+ private:
+ RunLoop* run_loop_;
+ MessagePipe* pipe_;
+ size_t depth_;
+ bool reached_depth_limit_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(NestingRunLoopHandler);
+};
+
+const size_t NestingRunLoopHandler::kDepthLimit = 10;
+const char NestingRunLoopHandler::kSignalMagic = 'X';
+
+TEST_F(RunLoopTest, NestedRun) {
+ NestingRunLoopHandler handler;
+ MessagePipe test_pipe;
+ RunLoop run_loop;
+ handler.set_run_loop(&run_loop);
+ handler.set_pipe(&test_pipe);
+ run_loop.AddHandler(&handler, test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ static_cast<MojoDeadline>(10000));
+ handler.WriteSignal();
+ run_loop.Run();
+
+ EXPECT_TRUE(handler.reached_depth_limit());
+ // Got MOJO_RESULT_DEADLINE_EXCEEDED once then removed from the
+ // RunLoop's handler list.
+ EXPECT_EQ(handler.error_count(), 1);
+ EXPECT_EQ(handler.last_error_result(), MOJO_RESULT_DEADLINE_EXCEEDED);
+}
+
+struct Task {
+ Task(int num, std::vector<int>* sequence) : num(num), sequence(sequence) {}
+
+ void Run() const { sequence->push_back(num); }
+
+ int num;
+ std::vector<int>* sequence;
+};
+
+TEST_F(RunLoopTest, DelayedTaskOrder) {
+ std::vector<int> sequence;
+ RunLoop run_loop;
+ run_loop.PostDelayedTask(Closure(Task(1, &sequence)), 0);
+ run_loop.PostDelayedTask(Closure(Task(2, &sequence)), 0);
+ run_loop.PostDelayedTask(Closure(Task(3, &sequence)), 0);
+ run_loop.RunUntilIdle();
+
+ ASSERT_EQ(3u, sequence.size());
+ EXPECT_EQ(1, sequence[0]);
+ EXPECT_EQ(2, sequence[1]);
+ EXPECT_EQ(3, sequence[2]);
+}
+
+struct QuittingTask {
+ explicit QuittingTask(RunLoop* run_loop) : run_loop(run_loop) {}
+
+ void Run() const { run_loop->Quit(); }
+
+ RunLoop* run_loop;
+};
+
+TEST_F(RunLoopTest, QuitFromDelayedTask) {
+ TestRunLoopHandler handler;
+ MessagePipe test_pipe;
+ RunLoop run_loop;
+ run_loop.AddHandler(&handler,
+ test_pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ run_loop.PostDelayedTask(Closure(QuittingTask(&run_loop)), 0);
+ run_loop.Run();
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/tests/thread_unittest.cc b/mojo/public/cpp/utility/tests/thread_unittest.cc
new file mode 100644
index 0000000..57c4ad9
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/thread_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright 2014 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/utility/thread.h"
+
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class SetIntThread : public Thread {
+ public:
+ SetIntThread(int* int_to_set, int value)
+ : int_to_set_(int_to_set),
+ value_(value) {
+ }
+ SetIntThread(const Options& options, int* int_to_set, int value)
+ : Thread(options),
+ int_to_set_(int_to_set),
+ value_(value) {
+ }
+
+ ~SetIntThread() override {}
+
+ void Run() override { *int_to_set_ = value_; }
+
+ private:
+ int* const int_to_set_;
+ const int value_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(SetIntThread);
+};
+
+TEST(ThreadTest, CreateAndJoin) {
+ int value = 0;
+
+ // Not starting the thread should result in a no-op.
+ {
+ SetIntThread thread(&value, 1234567);
+ }
+ EXPECT_EQ(0, value);
+
+ // Start and join.
+ {
+ SetIntThread thread(&value, 12345678);
+ thread.Start();
+ thread.Join();
+ EXPECT_EQ(12345678, value);
+ }
+
+ // Ditto, with non-default (but reasonable) stack size.
+ {
+ Thread::Options options;
+ options.set_stack_size(1024 * 1024); // 1 MB.
+ SetIntThread thread(options, &value, 12345678);
+ thread.Start();
+ thread.Join();
+ EXPECT_EQ(12345678, value);
+ }
+}
+
+// Tests of assertions for Debug builds.
+// Note: It's okay to create threads, despite gtest having to fork. (The threads
+// are in the child process.)
+#if !defined(NDEBUG)
+TEST(ThreadTest, DebugAssertionFailures) {
+ // Can only start once.
+ EXPECT_DEATH_IF_SUPPORTED({
+ int value = 0;
+ SetIntThread thread(&value, 1);
+ thread.Start();
+ thread.Start();
+ }, "");
+
+ // Must join (if you start).
+ EXPECT_DEATH_IF_SUPPORTED({
+ int value = 0;
+ SetIntThread thread(&value, 2);
+ thread.Start();
+ }, "");
+
+ // Can only join once.
+ EXPECT_DEATH_IF_SUPPORTED({
+ int value = 0;
+ SetIntThread thread(&value, 3);
+ thread.Start();
+ thread.Join();
+ thread.Join();
+ }, "");
+
+ // Stack too big (we're making certain assumptions here).
+ EXPECT_DEATH_IF_SUPPORTED({
+ int value = 0;
+ Thread::Options options;
+ options.set_stack_size(static_cast<size_t>(-1));
+ SetIntThread thread(options, &value, 4);
+ thread.Start();
+ thread.Join();
+ }, "");
+}
+#endif // !defined(NDEBUG)
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/cpp/utility/thread.h b/mojo/public/cpp/utility/thread.h
new file mode 100644
index 0000000..b7d10ee
--- /dev/null
+++ b/mojo/public/cpp/utility/thread.h
@@ -0,0 +1,62 @@
+// Copyright 2014 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_UTILITY_THREAD_H_
+#define MOJO_PUBLIC_CPP_UTILITY_THREAD_H_
+
+#ifdef _WIN32
+#error "Not implemented: See crbug.com/342893."
+#endif
+
+#include <pthread.h>
+#include <stddef.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// This class is thread-friendly, not thread-safe (e.g., you mustn't call
+// |Join()| from multiple threads and/or simultaneously try to destroy the
+// object).
+class Thread {
+ public:
+ // TODO(vtl): Support non-joinable? priority?
+ class Options {
+ public:
+ Options() : stack_size_(0) {}
+
+ // A stack size of 0 means the default.
+ size_t stack_size() const { return stack_size_; }
+ void set_stack_size(size_t stack_size) { stack_size_ = stack_size; }
+
+ private:
+ size_t stack_size_;
+
+ // Copy and assign allowed.
+ };
+
+ // TODO(vtl): Add name or name prefix?
+ Thread();
+ explicit Thread(const Options& options);
+ virtual ~Thread();
+
+ void Start();
+ void Join();
+
+ virtual void Run() = 0;
+
+ private:
+ static void* ThreadRunTrampoline(void* arg);
+
+ const Options options_;
+ pthread_t thread_;
+ bool started_;
+ bool joined_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_THREAD_H_