Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn
new file mode 100644
index 0000000..1874a6c
--- /dev/null
+++ b/mojo/public/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.
+
+group("public") {
+ # Meta-target, don't link into production code.
+ testonly = true
+ deps = [
+ ":libmojo_sdk",
+ ":sdk",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/environment:standalone",
+ "//mojo/public/cpp/utility",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ ]
+
+ if (is_linux) {
+ deps += [
+ "//mojo/public/python",
+ ]
+ }
+
+ if (is_android) {
+ deps += [
+ "//mojo/public/java:system",
+ "//mojo/public/java:bindings",
+ ]
+ }
+}
+
+group("sdk") {
+ deps = [
+ "//mojo/public/c/system",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/environment:standalone",
+ "//mojo/public/cpp/utility",
+ "//mojo/public/interfaces/application",
+ "//mojo/public/js/bindings",
+ ]
+}
+
+static_library("libmojo_sdk") {
+ complete_static_lib = true
+ deps = [ ":sdk" ]
+}
diff --git a/mojo/public/DEPS b/mojo/public/DEPS
new file mode 100644
index 0000000..0c679b9
--- /dev/null
+++ b/mojo/public/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "-base",
+ "-build",
+ "-mojo",
+ "+mojo/public",
+]
diff --git a/mojo/public/README.md b/mojo/public/README.md
new file mode 100644
index 0000000..a31a8a8
--- /dev/null
+++ b/mojo/public/README.md
@@ -0,0 +1,43 @@
+Mojo Public API
+===============
+
+The Mojo Public API is a binary stable API to the Mojo system.
+
+It consists of support for a number of programming languages (with a directory
+for each support language), some "build" tools and build-time requirements, and
+interface definitions for Mojo services (specified using an IDL).
+
+Note that there are various subdirectories named tests/. These contain tests of
+the code in the enclosing directory, and are not meant for use by Mojo
+applications.
+
+C/CPP/JS
+--------
+
+The c/, cpp/, js/ subdirectories define the API for C, C++, and JavaScript,
+respectively.
+
+The basic principle for these directories is that they consist of the source
+files that one needs at build/deployment/run time (as appropriate for the
+language), organized in a natural way for the particular language.
+
+Interfaces
+----------
+
+The interfaces/ subdirectory contains Mojo IDL (a.k.a. .mojom) descriptions of
+standard Mojo services.
+
+Platform
+--------
+
+The platform/ subdirectory contains any build-time requirements (e.g., static
+libraries) that may be needed to produce a Mojo application for certain
+platforms, such as a native shared library or as a NaCl binary.
+
+Tools
+-----
+
+The tools/ subdirectory contains tools that are useful/necessary at
+build/deployment time. These tools may be needed (as a practical necessity) to
+use the API in any given language, e.g., to generate bindings from Mojo IDL
+files.
diff --git a/mojo/public/c/DEPS b/mojo/public/c/DEPS
new file mode 100644
index 0000000..5272770
--- /dev/null
+++ b/mojo/public/c/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+ # Require explicit dependencies in each directory.
+ "-mojo/public",
+ # But everyone can depend on the C system headers.
+ "+mojo/public/c/system",
+]
+
+specific_include_rules = {
+ r".*_(unit|perf)test\.cc": [
+ "+testing",
+ # Our test harness is C++, so allow the use of C++:
+ "+mojo/public/cpp/system",
+ "+mojo/public/cpp/test_support",
+ "+mojo/public/cpp/utility",
+ ],
+}
diff --git a/mojo/public/c/PRESUBMIT.py b/mojo/public/c/PRESUBMIT.py
new file mode 100644
index 0000000..4cba433
--- /dev/null
+++ b/mojo/public/c/PRESUBMIT.py
@@ -0,0 +1,16 @@
+# 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.
+
+"""Presubmit script for mojo/public/c.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def CheckChangeOnUpload(input_api, output_api):
+ results = []
+ results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api,
+ output_api)
+ results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
+ return results
diff --git a/mojo/public/c/README.md b/mojo/public/c/README.md
new file mode 100644
index 0000000..8e11545
--- /dev/null
+++ b/mojo/public/c/README.md
@@ -0,0 +1,45 @@
+Mojo Public C API
+=================
+
+This directory contains C language bindings for the Mojo Public API.
+
+Environment
+-----------
+
+The environment/ subdirectory defines some common things that, while not part of
+the system API, may be required for GLES2 (for example). These are things that a
+Mojo application may be required to provide to the GLES2 (for example) library
+in order to use it. (However, the Mojo application may implement these things as
+it sees fit.)
+
+GLES2
+-----
+
+The gles2/ subdirectory defines the GLES2 C API that's available to Mojo
+applications. To use GLES2, Mojo applications must link against a dynamic
+library (the exact mechanism being platform-dependent) and use the header files
+in this directory as well as the standard Khronos GLES2 header files.
+
+The reason for this, rather than providing GLES2 using the standard Mojo IPC
+mechanism, is performance: The protocol (and transport mechanisms) used to
+communicate with the Mojo GLES2 service is not stable nor "public" (mainly for
+performance reasons), and using the dynamic library shields the application from
+changes to the underlying system.
+
+System
+------
+
+The system/ subdirectory provides definitions of the basic low-level API used by
+all Mojo applications (whether directly or indirectly). These consist primarily
+of the IPC primitives used to communicate with Mojo services.
+
+Though the message protocol is stable, the implementation of the transport is
+not, and access to the IPC mechanisms must be via the primitives defined in this
+directory.
+
+Test Support
+------------
+
+This directory contains a C API for running tests. This API is only available
+under special, specific test conditions. It is not meant for general use by Mojo
+applications.
diff --git a/mojo/public/c/environment/BUILD.gn b/mojo/public/c/environment/BUILD.gn
new file mode 100644
index 0000000..669da7d
--- /dev/null
+++ b/mojo/public/c/environment/BUILD.gn
@@ -0,0 +1,12 @@
+# 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.
+
+source_set("environment") {
+ sources = [
+ "async_waiter.h",
+ "logger.h",
+ ]
+
+ deps = [ "//mojo/public/c/system" ]
+}
diff --git a/mojo/public/c/environment/async_waiter.h b/mojo/public/c/environment/async_waiter.h
new file mode 100644
index 0000000..b5a1f75
--- /dev/null
+++ b/mojo/public/c/environment/async_waiter.h
@@ -0,0 +1,65 @@
+// 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_C_ENVIRONMENT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
+
+#include "mojo/public/c/system/types.h"
+
+typedef uintptr_t MojoAsyncWaitID;
+
+typedef void (*MojoAsyncWaitCallback)(void* closure, MojoResult result);
+
+// Functions for asynchronously waiting (and cancelling asynchronous waits) on a
+// handle.
+//
+// Thread-safety:
+// - |CancelWait(wait_id)| may only be called on the same thread as the
+// |AsyncWait()| that provided |wait_id| was called on.
+// - A given |MojoAsyncWaiter|'s functions may only be called on the thread(s)
+// that it is defined to be valid on (typically including the thread on
+// which the |MojoAsyncWaiter| was provided). E.g., a library may require
+// initialization with a single |MojoAsyncWaiter| and stipulate that it only
+// be used on threads on which that |MojoAsyncWaiter| is valid.
+// - If a |MojoAsyncWaiter| is valid on multiple threads, then its functions
+// must be thread-safe (subject to the first restriction above).
+struct MojoAsyncWaiter {
+ // Arranges for |callback| to be called on the current thread at some future
+ // when |handle| satisfies |signals| or it is known that it will never satisfy
+ // |signals| (with the same behavior as |MojoWait()|).
+ //
+ // |callback| will not be called in the nested context of |AsyncWait()|, but
+ // only, e.g., from some run loop. |callback| is provided with the |closure|
+ // argument as well as the result of the wait. For each call to |AsyncWait()|,
+ // |callback| will be called at most once.
+ //
+ // |handle| must not be closed or transferred (via |MojoWriteMessage()|; this
+ // is equivalent to closing the handle) until either the callback has been
+ // executed or the async wait has been cancelled using the returned (nonzero)
+ // |MojoAsyncWaitID| (see |CancelWait()|). Otherwise, an invalid (or, worse,
+ // re-used) handle may be waited on by the implementation of this
+ // |MojoAsyncWaiter|.
+ //
+ // Note that once the callback has been called, the returned |MojoAsyncWaitID|
+ // becomes invalid.
+ MojoAsyncWaitID (*AsyncWait)(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ MojoAsyncWaitCallback callback,
+ void* closure);
+
+ // Cancels an outstanding async wait (specified by |wait_id|) initiated by
+ // |AsyncWait()|. This may only be called from the same thread on which the
+ // corresponding |AsyncWait()| was called, and may only be called if the
+ // callback to |AsyncWait()| has not been called.
+ //
+ // Once this has been called, the callback provided to |AsyncWait()| will not
+ // be called. Moreover, it is then immediately safe to close or transfer the
+ // handle provided to |AsyncWait()|. (I.e., the implementation of this
+ // |MojoAsyncWaiter| will no longer wait on, or do anything else with, the
+ // handle.)
+ void (*CancelWait)(MojoAsyncWaitID wait_id);
+};
+
+#endif // MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
diff --git a/mojo/public/c/environment/logger.h b/mojo/public/c/environment/logger.h
new file mode 100644
index 0000000..c492a66
--- /dev/null
+++ b/mojo/public/c/environment/logger.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_C_ENVIRONMENT_LOGGER_H_
+#define MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
+
+#include <stdint.h>
+
+// |MojoLogLevel|: Used to specify the type of log message. Values are ordered
+// by severity (i.e., higher numerical values are more severe).
+
+typedef int32_t MojoLogLevel;
+
+#ifdef __cplusplus
+const MojoLogLevel MOJO_LOG_LEVEL_VERBOSE = -1;
+const MojoLogLevel MOJO_LOG_LEVEL_INFO = 0;
+const MojoLogLevel MOJO_LOG_LEVEL_WARNING = 1;
+const MojoLogLevel MOJO_LOG_LEVEL_ERROR = 2;
+const MojoLogLevel MOJO_LOG_LEVEL_FATAL = 3;
+#else
+#define MOJO_LOG_LEVEL_VERBOSE ((MojoLogLevel) - 1)
+#define MOJO_LOG_LEVEL_INFO ((MojoLogLevel)0)
+#define MOJO_LOG_LEVEL_WARNING ((MojoLogLevel)1)
+#define MOJO_LOG_LEVEL_ERROR ((MojoLogLevel)2)
+#define MOJO_LOG_LEVEL_FATAL ((MojoLogLevel)3)
+#endif
+
+// Structure with basic logging functions (on top of which more friendly logging
+// macros may be built). The functions are thread-safe, except for
+// |SetMinimumLogLevel()| (see below).
+struct MojoLogger {
+ // Logs |message| at level |log_level| if |log_level| is at least the current
+ // minimum log level. If |log_level| is |MOJO_LOG_LEVEL_FATAL| (or greater),
+ // aborts the application/process.
+ void (*LogMessage)(MojoLogLevel log_level, const char* message);
+
+ // Gets the minimum log level (see above), which will always be at most
+ // |MOJO_LOG_LEVEL_FATAL|. (Though |LogMessage()| will automatically avoid
+ // logging messages below the minimum log level, this may be used to avoid
+ // extra work.)
+ MojoLogLevel (*GetMinimumLogLevel)(void);
+
+ // Sets the minimum log level (see above) to the lesser of |minimum_log_level|
+ // and |MOJO_LOG_LEVEL_FATAL|.
+ //
+ // Warning: This function may not be thread-safe, and should not be called
+ // concurrently with other |MojoLogger| functions. (In some environments --
+ // such as Chromium -- that share a logger across applications, this may mean
+ // that it is almost never safe to call this.)
+ void (*SetMinimumLogLevel)(MojoLogLevel minimum_log_level);
+};
+
+#endif // MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
diff --git a/mojo/public/c/gles2/BUILD.gn b/mojo/public/c/gles2/BUILD.gn
new file mode 100644
index 0000000..df0007b
--- /dev/null
+++ b/mojo/public/c/gles2/BUILD.gn
@@ -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.
+
+config("gles2_config") {
+ defines = [ "GLES2_USE_MOJO" ]
+}
+
+source_set("gles2") {
+ sources = [
+ "gles2.h",
+ "gles2_export.h",
+ ]
+
+ public_configs = [ ":gles2_config" ]
+
+ public_deps = [
+ "//mojo/public/c/environment",
+ "//mojo/public/c/system",
+ ]
+}
diff --git a/mojo/public/c/gles2/DEPS b/mojo/public/c/gles2/DEPS
new file mode 100644
index 0000000..3887457
--- /dev/null
+++ b/mojo/public/c/gles2/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/c/environment",
+]
diff --git a/mojo/public/c/gles2/chromium_sync_point.h b/mojo/public/c/gles2/chromium_sync_point.h
new file mode 100644
index 0000000..6369712
--- /dev/null
+++ b/mojo/public/c/gles2/chromium_sync_point.h
@@ -0,0 +1,30 @@
+// 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_C_GLES2_CHROMIUM_SYNC_POINT_H_
+#define MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_
diff --git a/mojo/public/c/gles2/chromium_texture_mailbox.h b/mojo/public/c/gles2/chromium_texture_mailbox.h
new file mode 100644
index 0000000..177ebbb
--- /dev/null
+++ b/mojo/public/c/gles2/chromium_texture_mailbox.h
@@ -0,0 +1,30 @@
+// 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_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_
+#define MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_
diff --git a/mojo/public/c/gles2/gles2.h b/mojo/public/c/gles2/gles2.h
new file mode 100644
index 0000000..36c6f7f
--- /dev/null
+++ b/mojo/public/c/gles2/gles2.h
@@ -0,0 +1,45 @@
+// 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_C_GLES2_GLES2_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOJO_GLES2_EXPORT MojoGLES2Context
+ MojoGLES2CreateContext(MojoHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ void* closure,
+ const MojoAsyncWaiter* async_waiter);
+MOJO_GLES2_EXPORT void MojoGLES2DestroyContext(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2MakeCurrent(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2SwapBuffers(void);
+
+// TODO(piman): We shouldn't have to leak those 2 interfaces, especially in a
+// type-unsafe way.
+MOJO_GLES2_EXPORT void* MojoGLES2GetGLES2Interface(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void* MojoGLES2GetContextSupport(MojoGLES2Context context);
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_GLES2_GLES2_H_
diff --git a/mojo/public/c/gles2/gles2_call_visitor_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_autogen.h
new file mode 100644
index 0000000..72494c5
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_call_visitor_autogen.h
@@ -0,0 +1,544 @@
+// 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 is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(ActiveTexture, void, (GLenum texture), (texture))
+VISIT_GL_CALL(AttachShader,
+ void,
+ (GLuint program, GLuint shader),
+ (program, shader))
+VISIT_GL_CALL(BindAttribLocation,
+ void,
+ (GLuint program, GLuint index, const char* name),
+ (program, index, name))
+VISIT_GL_CALL(BindBuffer,
+ void,
+ (GLenum target, GLuint buffer),
+ (target, buffer))
+VISIT_GL_CALL(BindFramebuffer,
+ void,
+ (GLenum target, GLuint framebuffer),
+ (target, framebuffer))
+VISIT_GL_CALL(BindRenderbuffer,
+ void,
+ (GLenum target, GLuint renderbuffer),
+ (target, renderbuffer))
+VISIT_GL_CALL(BindTexture,
+ void,
+ (GLenum target, GLuint texture),
+ (target, texture))
+VISIT_GL_CALL(BlendColor,
+ void,
+ (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha),
+ (red, green, blue, alpha))
+VISIT_GL_CALL(BlendEquation, void, (GLenum mode), (mode))
+VISIT_GL_CALL(BlendEquationSeparate,
+ void,
+ (GLenum modeRGB, GLenum modeAlpha),
+ (modeRGB, modeAlpha))
+VISIT_GL_CALL(BlendFunc,
+ void,
+ (GLenum sfactor, GLenum dfactor),
+ (sfactor, dfactor))
+VISIT_GL_CALL(BlendFuncSeparate,
+ void,
+ (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha),
+ (srcRGB, dstRGB, srcAlpha, dstAlpha))
+VISIT_GL_CALL(BufferData,
+ void,
+ (GLenum target, GLsizeiptr size, const void* data, GLenum usage),
+ (target, size, data, usage))
+VISIT_GL_CALL(
+ BufferSubData,
+ void,
+ (GLenum target, GLintptr offset, GLsizeiptr size, const void* data),
+ (target, offset, size, data))
+VISIT_GL_CALL(CheckFramebufferStatus, GLenum, (GLenum target), (target))
+VISIT_GL_CALL(Clear, void, (GLbitfield mask), (mask))
+VISIT_GL_CALL(ClearColor,
+ void,
+ (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha),
+ (red, green, blue, alpha))
+VISIT_GL_CALL(ClearDepthf, void, (GLclampf depth), (depth))
+VISIT_GL_CALL(ClearStencil, void, (GLint s), (s))
+VISIT_GL_CALL(ColorMask,
+ void,
+ (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha),
+ (red, green, blue, alpha))
+VISIT_GL_CALL(CompileShader, void, (GLuint shader), (shader))
+VISIT_GL_CALL(
+ CompressedTexImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void* data),
+ (target, level, internalformat, width, height, border, imageSize, data))
+VISIT_GL_CALL(
+ CompressedTexSubImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data),
+ (target, level, xoffset, yoffset, width, height, format, imageSize, data))
+VISIT_GL_CALL(CopyTexImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border),
+ (target, level, internalformat, x, y, width, height, border))
+VISIT_GL_CALL(CopyTexSubImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height),
+ (target, level, xoffset, yoffset, x, y, width, height))
+VISIT_GL_CALL(CreateProgram, GLuint, (), ())
+VISIT_GL_CALL(CreateShader, GLuint, (GLenum type), (type))
+VISIT_GL_CALL(CullFace, void, (GLenum mode), (mode))
+VISIT_GL_CALL(DeleteBuffers,
+ void,
+ (GLsizei n, const GLuint* buffers),
+ (n, buffers))
+VISIT_GL_CALL(DeleteFramebuffers,
+ void,
+ (GLsizei n, const GLuint* framebuffers),
+ (n, framebuffers))
+VISIT_GL_CALL(DeleteProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(DeleteRenderbuffers,
+ void,
+ (GLsizei n, const GLuint* renderbuffers),
+ (n, renderbuffers))
+VISIT_GL_CALL(DeleteShader, void, (GLuint shader), (shader))
+VISIT_GL_CALL(DeleteTextures,
+ void,
+ (GLsizei n, const GLuint* textures),
+ (n, textures))
+VISIT_GL_CALL(DepthFunc, void, (GLenum func), (func))
+VISIT_GL_CALL(DepthMask, void, (GLboolean flag), (flag))
+VISIT_GL_CALL(DepthRangef, void, (GLclampf zNear, GLclampf zFar), (zNear, zFar))
+VISIT_GL_CALL(DetachShader,
+ void,
+ (GLuint program, GLuint shader),
+ (program, shader))
+VISIT_GL_CALL(Disable, void, (GLenum cap), (cap))
+VISIT_GL_CALL(DisableVertexAttribArray, void, (GLuint index), (index))
+VISIT_GL_CALL(DrawArrays,
+ void,
+ (GLenum mode, GLint first, GLsizei count),
+ (mode, first, count))
+VISIT_GL_CALL(DrawElements,
+ void,
+ (GLenum mode, GLsizei count, GLenum type, const void* indices),
+ (mode, count, type, indices))
+VISIT_GL_CALL(Enable, void, (GLenum cap), (cap))
+VISIT_GL_CALL(EnableVertexAttribArray, void, (GLuint index), (index))
+VISIT_GL_CALL(Finish, void, (), ())
+VISIT_GL_CALL(Flush, void, (), ())
+VISIT_GL_CALL(FramebufferRenderbuffer,
+ void,
+ (GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer),
+ (target, attachment, renderbuffertarget, renderbuffer))
+VISIT_GL_CALL(FramebufferTexture2D,
+ void,
+ (GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level),
+ (target, attachment, textarget, texture, level))
+VISIT_GL_CALL(FrontFace, void, (GLenum mode), (mode))
+VISIT_GL_CALL(GenBuffers, void, (GLsizei n, GLuint * buffers), (n, buffers))
+VISIT_GL_CALL(GenerateMipmap, void, (GLenum target), (target))
+VISIT_GL_CALL(GenFramebuffers,
+ void,
+ (GLsizei n, GLuint * framebuffers),
+ (n, framebuffers))
+VISIT_GL_CALL(GenRenderbuffers,
+ void,
+ (GLsizei n, GLuint * renderbuffers),
+ (n, renderbuffers))
+VISIT_GL_CALL(GenTextures, void, (GLsizei n, GLuint * textures), (n, textures))
+VISIT_GL_CALL(GetActiveAttrib,
+ void,
+ (GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei * length,
+ GLint * size,
+ GLenum * type,
+ char* name),
+ (program, index, bufsize, length, size, type, name))
+VISIT_GL_CALL(GetActiveUniform,
+ void,
+ (GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei * length,
+ GLint * size,
+ GLenum * type,
+ char* name),
+ (program, index, bufsize, length, size, type, name))
+VISIT_GL_CALL(
+ GetAttachedShaders,
+ void,
+ (GLuint program, GLsizei maxcount, GLsizei * count, GLuint * shaders),
+ (program, maxcount, count, shaders))
+VISIT_GL_CALL(GetAttribLocation,
+ GLint,
+ (GLuint program, const char* name),
+ (program, name))
+VISIT_GL_CALL(GetBooleanv,
+ void,
+ (GLenum pname, GLboolean * params),
+ (pname, params))
+VISIT_GL_CALL(GetBufferParameteriv,
+ void,
+ (GLenum target, GLenum pname, GLint * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetError, GLenum, (), ())
+VISIT_GL_CALL(GetFloatv,
+ void,
+ (GLenum pname, GLfloat * params),
+ (pname, params))
+VISIT_GL_CALL(GetFramebufferAttachmentParameteriv,
+ void,
+ (GLenum target, GLenum attachment, GLenum pname, GLint * params),
+ (target, attachment, pname, params))
+VISIT_GL_CALL(GetIntegerv,
+ void,
+ (GLenum pname, GLint * params),
+ (pname, params))
+VISIT_GL_CALL(GetProgramiv,
+ void,
+ (GLuint program, GLenum pname, GLint * params),
+ (program, pname, params))
+VISIT_GL_CALL(
+ GetProgramInfoLog,
+ void,
+ (GLuint program, GLsizei bufsize, GLsizei * length, char* infolog),
+ (program, bufsize, length, infolog))
+VISIT_GL_CALL(GetRenderbufferParameteriv,
+ void,
+ (GLenum target, GLenum pname, GLint * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetShaderiv,
+ void,
+ (GLuint shader, GLenum pname, GLint * params),
+ (shader, pname, params))
+VISIT_GL_CALL(GetShaderInfoLog,
+ void,
+ (GLuint shader, GLsizei bufsize, GLsizei * length, char* infolog),
+ (shader, bufsize, length, infolog))
+VISIT_GL_CALL(
+ GetShaderPrecisionFormat,
+ void,
+ (GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision),
+ (shadertype, precisiontype, range, precision))
+VISIT_GL_CALL(GetShaderSource,
+ void,
+ (GLuint shader, GLsizei bufsize, GLsizei * length, char* source),
+ (shader, bufsize, length, source))
+VISIT_GL_CALL(GetString, const GLubyte*, (GLenum name), (name))
+VISIT_GL_CALL(GetTexParameterfv,
+ void,
+ (GLenum target, GLenum pname, GLfloat * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetTexParameteriv,
+ void,
+ (GLenum target, GLenum pname, GLint * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetUniformfv,
+ void,
+ (GLuint program, GLint location, GLfloat * params),
+ (program, location, params))
+VISIT_GL_CALL(GetUniformiv,
+ void,
+ (GLuint program, GLint location, GLint * params),
+ (program, location, params))
+VISIT_GL_CALL(GetUniformLocation,
+ GLint,
+ (GLuint program, const char* name),
+ (program, name))
+VISIT_GL_CALL(GetVertexAttribfv,
+ void,
+ (GLuint index, GLenum pname, GLfloat * params),
+ (index, pname, params))
+VISIT_GL_CALL(GetVertexAttribiv,
+ void,
+ (GLuint index, GLenum pname, GLint * params),
+ (index, pname, params))
+VISIT_GL_CALL(GetVertexAttribPointerv,
+ void,
+ (GLuint index, GLenum pname, void** pointer),
+ (index, pname, pointer))
+VISIT_GL_CALL(Hint, void, (GLenum target, GLenum mode), (target, mode))
+VISIT_GL_CALL(IsBuffer, GLboolean, (GLuint buffer), (buffer))
+VISIT_GL_CALL(IsEnabled, GLboolean, (GLenum cap), (cap))
+VISIT_GL_CALL(IsFramebuffer, GLboolean, (GLuint framebuffer), (framebuffer))
+VISIT_GL_CALL(IsProgram, GLboolean, (GLuint program), (program))
+VISIT_GL_CALL(IsRenderbuffer, GLboolean, (GLuint renderbuffer), (renderbuffer))
+VISIT_GL_CALL(IsShader, GLboolean, (GLuint shader), (shader))
+VISIT_GL_CALL(IsTexture, GLboolean, (GLuint texture), (texture))
+VISIT_GL_CALL(LineWidth, void, (GLfloat width), (width))
+VISIT_GL_CALL(LinkProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(PixelStorei, void, (GLenum pname, GLint param), (pname, param))
+VISIT_GL_CALL(PolygonOffset,
+ void,
+ (GLfloat factor, GLfloat units),
+ (factor, units))
+VISIT_GL_CALL(ReadPixels,
+ void,
+ (GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ void* pixels),
+ (x, y, width, height, format, type, pixels))
+VISIT_GL_CALL(ReleaseShaderCompiler, void, (), ())
+VISIT_GL_CALL(
+ RenderbufferStorage,
+ void,
+ (GLenum target, GLenum internalformat, GLsizei width, GLsizei height),
+ (target, internalformat, width, height))
+VISIT_GL_CALL(SampleCoverage,
+ void,
+ (GLclampf value, GLboolean invert),
+ (value, invert))
+VISIT_GL_CALL(Scissor,
+ void,
+ (GLint x, GLint y, GLsizei width, GLsizei height),
+ (x, y, width, height))
+VISIT_GL_CALL(ShaderBinary,
+ void,
+ (GLsizei n,
+ const GLuint* shaders,
+ GLenum binaryformat,
+ const void* binary,
+ GLsizei length),
+ (n, shaders, binaryformat, binary, length))
+VISIT_GL_CALL(ShaderSource,
+ void,
+ (GLuint shader,
+ GLsizei count,
+ const GLchar* const* str,
+ const GLint* length),
+ (shader, count, str, length))
+VISIT_GL_CALL(StencilFunc,
+ void,
+ (GLenum func, GLint ref, GLuint mask),
+ (func, ref, mask))
+VISIT_GL_CALL(StencilFuncSeparate,
+ void,
+ (GLenum face, GLenum func, GLint ref, GLuint mask),
+ (face, func, ref, mask))
+VISIT_GL_CALL(StencilMask, void, (GLuint mask), (mask))
+VISIT_GL_CALL(StencilMaskSeparate,
+ void,
+ (GLenum face, GLuint mask),
+ (face, mask))
+VISIT_GL_CALL(StencilOp,
+ void,
+ (GLenum fail, GLenum zfail, GLenum zpass),
+ (fail, zfail, zpass))
+VISIT_GL_CALL(StencilOpSeparate,
+ void,
+ (GLenum face, GLenum fail, GLenum zfail, GLenum zpass),
+ (face, fail, zfail, zpass))
+VISIT_GL_CALL(TexImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels),
+ (target,
+ level,
+ internalformat,
+ width,
+ height,
+ border,
+ format,
+ type,
+ pixels))
+VISIT_GL_CALL(TexParameterf,
+ void,
+ (GLenum target, GLenum pname, GLfloat param),
+ (target, pname, param))
+VISIT_GL_CALL(TexParameterfv,
+ void,
+ (GLenum target, GLenum pname, const GLfloat* params),
+ (target, pname, params))
+VISIT_GL_CALL(TexParameteri,
+ void,
+ (GLenum target, GLenum pname, GLint param),
+ (target, pname, param))
+VISIT_GL_CALL(TexParameteriv,
+ void,
+ (GLenum target, GLenum pname, const GLint* params),
+ (target, pname, params))
+VISIT_GL_CALL(
+ TexSubImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void* pixels),
+ (target, level, xoffset, yoffset, width, height, format, type, pixels))
+VISIT_GL_CALL(Uniform1f, void, (GLint location, GLfloat x), (location, x))
+VISIT_GL_CALL(Uniform1fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform1i, void, (GLint location, GLint x), (location, x))
+VISIT_GL_CALL(Uniform1iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform2f,
+ void,
+ (GLint location, GLfloat x, GLfloat y),
+ (location, x, y))
+VISIT_GL_CALL(Uniform2fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform2i,
+ void,
+ (GLint location, GLint x, GLint y),
+ (location, x, y))
+VISIT_GL_CALL(Uniform2iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform3f,
+ void,
+ (GLint location, GLfloat x, GLfloat y, GLfloat z),
+ (location, x, y, z))
+VISIT_GL_CALL(Uniform3fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform3i,
+ void,
+ (GLint location, GLint x, GLint y, GLint z),
+ (location, x, y, z))
+VISIT_GL_CALL(Uniform3iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform4f,
+ void,
+ (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w),
+ (location, x, y, z, w))
+VISIT_GL_CALL(Uniform4fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform4i,
+ void,
+ (GLint location, GLint x, GLint y, GLint z, GLint w),
+ (location, x, y, z, w))
+VISIT_GL_CALL(Uniform4iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(
+ UniformMatrix2fv,
+ void,
+ (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+ (location, count, transpose, value))
+VISIT_GL_CALL(
+ UniformMatrix3fv,
+ void,
+ (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+ (location, count, transpose, value))
+VISIT_GL_CALL(
+ UniformMatrix4fv,
+ void,
+ (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+ (location, count, transpose, value))
+VISIT_GL_CALL(UseProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(ValidateProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(VertexAttrib1f, void, (GLuint indx, GLfloat x), (indx, x))
+VISIT_GL_CALL(VertexAttrib1fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttrib2f,
+ void,
+ (GLuint indx, GLfloat x, GLfloat y),
+ (indx, x, y))
+VISIT_GL_CALL(VertexAttrib2fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttrib3f,
+ void,
+ (GLuint indx, GLfloat x, GLfloat y, GLfloat z),
+ (indx, x, y, z))
+VISIT_GL_CALL(VertexAttrib3fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttrib4f,
+ void,
+ (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w),
+ (indx, x, y, z, w))
+VISIT_GL_CALL(VertexAttrib4fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttribPointer,
+ void,
+ (GLuint indx,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr),
+ (indx, size, type, normalized, stride, ptr))
+VISIT_GL_CALL(Viewport,
+ void,
+ (GLint x, GLint y, GLsizei width, GLsizei height),
+ (x, y, width, height))
diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h
new file mode 100644
index 0000000..3c3c4b9
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h
@@ -0,0 +1,12 @@
+// 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 is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(InsertSyncPointCHROMIUM, GLuint, (), ())
+VISIT_GL_CALL(WaitSyncPointCHROMIUM, void, (GLuint sync_point), (sync_point))
diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h
new file mode 100644
index 0000000..184c2f2
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h
@@ -0,0 +1,27 @@
+// 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 is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(GenMailboxCHROMIUM, void, (GLbyte * mailbox), (mailbox))
+VISIT_GL_CALL(ProduceTextureCHROMIUM,
+ void,
+ (GLenum target, const GLbyte* mailbox),
+ (target, mailbox))
+VISIT_GL_CALL(ProduceTextureDirectCHROMIUM,
+ void,
+ (GLuint texture, GLenum target, const GLbyte* mailbox),
+ (texture, target, mailbox))
+VISIT_GL_CALL(ConsumeTextureCHROMIUM,
+ void,
+ (GLenum target, const GLbyte* mailbox),
+ (target, mailbox))
+VISIT_GL_CALL(CreateAndConsumeTextureCHROMIUM,
+ GLuint,
+ (GLenum target, const GLbyte* mailbox),
+ (target, mailbox))
diff --git a/mojo/public/c/gles2/gles2_export.h b/mojo/public/c/gles2/gles2_export.h
new file mode 100644
index 0000000..60667b1
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_export.h
@@ -0,0 +1,33 @@
+// 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_C_GLES2_GLES2_EXPORT_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
+
+#if defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL)
+#if defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPLEMENTATION)
+#define MOJO_GLES2_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GLES2_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPLEMENTATION)
+#define MOJO_GLES2_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GLES2_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_GLES2_IMPL)
+
+#define MOJO_GLES2_EXPORT
+
+#endif // defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL)
+
+#endif // MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
diff --git a/mojo/public/c/gles2/gles2_types.h b/mojo/public/c/gles2/gles2_types.h
new file mode 100644
index 0000000..3ecf4db
--- /dev/null
+++ b/mojo/public/c/gles2/gles2_types.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MojoGLES2ContextPrivate* MojoGLES2Context;
+typedef void (*MojoGLES2ContextLost)(void* closure);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
new file mode 100644
index 0000000..b47b3da
--- /dev/null
+++ b/mojo/public/c/system/BUILD.gn
@@ -0,0 +1,48 @@
+# 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.
+
+# Depend on this target to use the types etc defined in the system without
+# linking against a specific implementation of the system. To link against a
+# particular implementation, use the :for_component or
+# :for_shared_library targets, depending on the type of target you are.
+source_set("system") {
+ sources = [
+ "buffer.h",
+ "core.h",
+ "data_pipe.h",
+ "functions.h",
+ "macros.h",
+ "message_pipe.h",
+ "system_export.h",
+ "types.h",
+ ]
+}
+
+# In an is_component_build build, everything can link against //mojo/edk/system
+# because it is built as a shared library. However, in a static build,
+# //mojo/edk/system is linked into an executable (e.g., mojo_shell), and must be
+# injected into other shared libraries (i.e., Mojo Apps) that need the mojo
+# system API.
+#
+# For component targets, add //mojo/public/c/system:for_component to your deps
+# section.
+#
+# For shared_library targets (e.g., a Mojo App), add
+# //mojo/public/c/system:for_shared_library to your deps
+
+group("for_shared_library") {
+ public_deps = [ ":system" ]
+ if (is_component_build) {
+ deps = [ "//mojo/edk/system" ]
+ } else {
+ deps = [ "//mojo/public/platform/native:system_thunks" ]
+ }
+}
+
+group("for_component") {
+ public_deps = [ ":system" ]
+ if (is_component_build) {
+ deps = [ "//mojo/edk/system" ]
+ }
+}
diff --git a/mojo/public/c/system/buffer.h b/mojo/public/c/system/buffer.h
new file mode 100644
index 0000000..19e3c52
--- /dev/null
+++ b/mojo/public/c/system/buffer.h
@@ -0,0 +1,186 @@
+// 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 contains types/constants and functions specific to buffers (and in
+// particular shared buffers).
+// TODO(vtl): Reorganize this file (etc.) to separate general buffer functions
+// from (shared) buffer creation.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateSharedBufferOptions|: Used to specify creation parameters for a
+// shared buffer to |MojoCreateSharedBuffer()|.
+// |uint32_t struct_size|: Set to the size of the
+// |MojoCreateSharedBufferOptions| struct. (Used to allow for future
+// extensions.)
+// |MojoCreateSharedBufferOptionsFlags flags|: Reserved for future use.
+// |MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE|: No flags; default mode.
+//
+// TODO(vtl): Maybe add a flag to indicate whether the memory should be
+// executable or not?
+// TODO(vtl): Also a flag for discardable (ashmem-style) buffers.
+
+typedef uint32_t MojoCreateSharedBufferOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateSharedBufferOptionsFlags
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE \
+ ((MojoCreateSharedBufferOptionsFlags)0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateSharedBufferOptions {
+ uint32_t struct_size;
+ MojoCreateSharedBufferOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateSharedBufferOptions) == 8,
+ MojoCreateSharedBufferOptions_has_wrong_size);
+
+// |MojoDuplicateBufferHandleOptions|: Used to specify parameters in duplicating
+// access to a shared buffer to |MojoDuplicateBufferHandle()|.
+// |uint32_t struct_size|: Set to the size of the
+// |MojoDuplicateBufferHandleOptions| struct. (Used to allow for future
+// extensions.)
+// |MojoDuplicateBufferHandleOptionsFlags flags|: Reserved for future use.
+// |MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE|: No flags; default
+// mode.
+//
+// TODO(vtl): Add flags to remove writability (and executability)? Also, COW?
+
+typedef uint32_t MojoDuplicateBufferHandleOptionsFlags;
+
+#ifdef __cplusplus
+const MojoDuplicateBufferHandleOptionsFlags
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE \
+ ((MojoDuplicateBufferHandleOptionsFlags)0)
+#endif
+
+struct MojoDuplicateBufferHandleOptions {
+ uint32_t struct_size;
+ MojoDuplicateBufferHandleOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoDuplicateBufferHandleOptions) == 8,
+ MojoDuplicateBufferHandleOptions_has_wrong_size);
+
+// |MojoMapBufferFlags|: Used to specify different modes to |MojoMapBuffer()|.
+// |MOJO_MAP_BUFFER_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoMapBufferFlags;
+
+#ifdef __cplusplus
+const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE = 0;
+#else
+#define MOJO_MAP_BUFFER_FLAG_NONE ((MojoMapBufferFlags)0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a buffer of size |num_bytes| bytes that can be shared between
+// applications (by duplicating the handle -- see |MojoDuplicateBufferHandle()|
+// -- and passing it over a message pipe). To access the buffer, one must call
+// |MojoMapBuffer()|.
+//
+// |options| may be set to null for a shared buffer with the default options.
+//
+// On success, |*shared_buffer_handle| will be set to the handle for the shared
+// buffer. (On failure, it is not modified.)
+//
+// Note: While more than |num_bytes| bytes may apparently be
+// available/visible/readable/writable, trying to use those extra bytes is
+// undefined behavior.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |*options| is invalid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+// been reached (e.g., if the requested size was too large, or if the
+// maximum number of handles was exceeded).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateSharedBuffer(
+ const struct MojoCreateSharedBufferOptions* options, // Optional.
+ uint64_t num_bytes, // In.
+ MojoHandle* shared_buffer_handle); // Out.
+
+// Duplicates the handle |buffer_handle| to a buffer. This creates another
+// handle (returned in |*new_buffer_handle| on success), which can then be sent
+// to another application over a message pipe, while retaining access to the
+// |buffer_handle| (and any mappings that it may have).
+//
+// |options| may be set to null to duplicate the buffer handle with the default
+// options.
+//
+// On success, |*shared_buffer_handle| will be set to the handle for the new
+// buffer handle. (On failure, it is not modified.)
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |buffer_handle| is not a valid buffer handle or |*options| is invalid).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoDuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const struct MojoDuplicateBufferHandleOptions* options, // Optional.
+ MojoHandle* new_buffer_handle); // Out.
+
+// Maps the part (at offset |offset| of length |num_bytes|) of the buffer given
+// by |buffer_handle| into memory, with options specified by |flags|. |offset +
+// num_bytes| must be less than or equal to the size of the buffer. On success,
+// |*buffer| points to memory with the requested part of the buffer. (On
+// failure, it is not modified.)
+//
+// A single buffer handle may have multiple active mappings (possibly depending
+// on the buffer type). The permissions (e.g., writable or executable) of the
+// returned memory may depend on the properties of the buffer and properties
+// attached to the buffer handle as well as |flags|.
+//
+// Note: Though data outside the specified range may apparently be
+// available/visible/readable/writable, trying to use those extra bytes is
+// undefined behavior.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |buffer_handle| is not a valid buffer handle or the range specified by
+// |offset| and |num_bytes| is not valid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if the mapping operation itself failed
+// (e.g., due to not having appropriate address space available).
+MOJO_SYSTEM_EXPORT MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer, // Out.
+ MojoMapBufferFlags flags);
+
+// Unmaps a buffer pointer that was mapped by |MojoMapBuffer()|. |buffer| must
+// have been the result of |MojoMapBuffer()| (not some pointer strictly inside
+// the mapped memory), and the entire mapping will be removed (partial unmapping
+// is not supported). A mapping may only be unmapped exactly once.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |buffer| is invalid (e.g., is not the
+// result of |MojoMapBuffer()| or has already been unmapped).
+MOJO_SYSTEM_EXPORT MojoResult MojoUnmapBuffer(void* buffer); // In.
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
diff --git a/mojo/public/c/system/core.h b/mojo/public/c/system/core.h
new file mode 100644
index 0000000..0e78786
--- /dev/null
+++ b/mojo/public/c/system/core.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.
+
+// This is a catch-all header that includes everything.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_C_SYSTEM_CORE_H_
+
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#endif // MOJO_PUBLIC_C_SYSTEM_CORE_H_
diff --git a/mojo/public/c/system/data_pipe.h b/mojo/public/c/system/data_pipe.h
new file mode 100644
index 0000000..c8087ea
--- /dev/null
+++ b/mojo/public/c/system/data_pipe.h
@@ -0,0 +1,364 @@
+// 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 contains types/constants and functions specific to data pipes.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateDataPipeOptions|: Used to specify creation parameters for a data
+// pipe to |MojoCreateDataPipe()|.
+// |uint32_t struct_size|: Set to the size of the |MojoCreateDataPipeOptions|
+// struct. (Used to allow for future extensions.)
+// |MojoCreateDataPipeOptionsFlags flags|: Used to specify different modes of
+// operation.
+// |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode.
+// |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD|: May discard data for
+// whatever reason; best-effort delivery. In particular, if the capacity
+// is reached, old data may be discard to make room for new data.
+// |uint32_t element_num_bytes|: The size of an element, in bytes. All
+// transactions and buffers will consist of an integral number of
+// elements. Must be nonzero.
+// |uint32_t capacity_num_bytes|: The capacity of the data pipe, in number of
+// bytes; must be a multiple of |element_num_bytes|. The data pipe will
+// always be able to queue AT LEAST this much data. Set to zero to opt for
+// a system-dependent automatically-calculated capacity (which will always
+// be at least one element).
+
+typedef uint32_t MojoCreateDataPipeOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE =
+ 0;
+const MojoCreateDataPipeOptionsFlags
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD = 1 << 0;
+#else
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE \
+ ((MojoCreateDataPipeOptionsFlags)0)
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD \
+ ((MojoCreateDataPipeOptionsFlags)1 << 0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateDataPipeOptions {
+ uint32_t struct_size;
+ MojoCreateDataPipeOptionsFlags flags;
+ uint32_t element_num_bytes;
+ uint32_t capacity_num_bytes;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateDataPipeOptions) == 16,
+ MojoCreateDataPipeOptions_has_wrong_size);
+
+// |MojoWriteDataFlags|: Used to specify different modes to |MojoWriteData()|
+// and |MojoBeginWriteData()|.
+// |MOJO_WRITE_DATA_FLAG_NONE| - No flags; default mode.
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| - Write either all the elements
+// requested or none of them.
+
+typedef uint32_t MojoWriteDataFlags;
+
+#ifdef __cplusplus
+const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE = 0;
+const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE = 1 << 0;
+#else
+#define MOJO_WRITE_DATA_FLAG_NONE ((MojoWriteDataFlags)0)
+#define MOJO_WRITE_DATA_FLAG_ALL_OR_NONE ((MojoWriteDataFlags)1 << 0)
+#endif
+
+// |MojoReadDataFlags|: Used to specify different modes to |MojoReadData()| and
+// |MojoBeginReadData()|.
+// |MOJO_READ_DATA_FLAG_NONE| - No flags; default mode.
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| - Read (or discard) either the requested
+// number of elements or none.
+// |MOJO_READ_DATA_FLAG_DISCARD| - Discard (up to) the requested number of
+// elements.
+// |MOJO_READ_DATA_FLAG_QUERY| - Query the number of elements available to
+// read. For use with |MojoReadData()| only. Mutually exclusive with
+// |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is
+// ignored if this flag is set.
+
+typedef uint32_t MojoReadDataFlags;
+
+#ifdef __cplusplus
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE = 0;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE = 1 << 0;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD = 1 << 1;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY = 1 << 2;
+#else
+#define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags)0)
+#define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags)1 << 0)
+#define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags)1 << 1)
+#define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags)1 << 2)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a data pipe, which is a unidirectional communication channel for
+// unframed data, with the given options. Data is unframed, but must come as
+// (multiples of) discrete elements, of the size given in |options|. See
+// |MojoCreateDataPipeOptions| for a description of the different options
+// available for data pipes.
+//
+// |options| may be set to null for a data pipe with the default options (which
+// will have an element size of one byte and have some system-dependent
+// capacity).
+//
+// On success, |*data_pipe_producer_handle| will be set to the handle for the
+// producer and |*data_pipe_consumer_handle| will be set to the handle for the
+// consumer. (On failure, they are not modified.)
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |*options| is invalid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+// been reached (e.g., if the requested capacity was too large, or if the
+// maximum number of handles was exceeded).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateDataPipe(
+ const struct MojoCreateDataPipeOptions* options, // Optional.
+ MojoHandle* data_pipe_producer_handle, // Out.
+ MojoHandle* data_pipe_consumer_handle); // Out.
+
+// Writes the given data to the data pipe producer given by
+// |data_pipe_producer_handle|. |elements| points to data of size |*num_bytes|;
+// |*num_bytes| should be a multiple of the data pipe's element size. If
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is set in |flags|, either all the data
+// will be written or none is.
+//
+// On success, |*num_bytes| is set to the amount of data that was actually
+// written.
+//
+// Note: If the data pipe has the "may discard" option flag (specified on
+// creation), this will discard as much data as required to write the given
+// data, starting with the earliest written data that has not been consumed.
+// However, even with "may discard", if |*num_bytes| is greater than the data
+// pipe's capacity (and |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is not set), this
+// will write the maximum amount possible (namely, the data pipe's capacity) and
+// set |*num_bytes| to that amount. It will *not* discard data from |elements|.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_producer_dispatcher| is not a handle to a data pipe
+// producer or |*num_bytes| is not a multiple of the data pipe's element
+// size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been
+// closed.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data
+// (specified by |*num_bytes|) could not be written.
+// |MOJO_RESULT_BUSY| if there is a two-phase write ongoing with
+// |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been
+// called, but not yet the matching |MojoEndWriteData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the
+// consumer is still open) and |flags| does *not* have
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set.
+//
+// TODO(vtl): Should there be a way of querying how much data can be written?
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoWriteData(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_bytes, // In/out.
+ MojoWriteDataFlags flags);
+
+// Begins a two-phase write to the data pipe producer given by
+// |data_pipe_producer_handle|. On success, |*buffer| will be a pointer to which
+// the caller can write |*buffer_num_bytes| bytes of data. If flags has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, then the output value
+// |*buffer_num_bytes| will be at least as large as its input value, which must
+// also be a multiple of the element size (if |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE|
+// is not set, the input value of |*buffer_num_bytes| is ignored).
+//
+// During a two-phase write, |data_pipe_producer_handle| is *not* writable.
+// E.g., if another thread tries to write to it, it will get |MOJO_RESULT_BUSY|;
+// that thread can then wait for |data_pipe_producer_handle| to become writable
+// again.
+//
+// Once the caller has finished writing data to |*buffer|, it should call
+// |MojoEndWriteData()| to specify the amount written and to complete the
+// two-phase write.
+//
+// Note: If the data pipe has the "may discard" option flag (specified on
+// creation) and |flags| has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, this may
+// discard some data.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_producer_handle| is not a handle to a data pipe producer or
+// flags has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and
+// |*buffer_num_bytes| is not a multiple of the element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been
+// closed.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data
+// (specified by |*buffer_num_bytes|) cannot be written contiguously at
+// this time. (Note that there may be space available for the required
+// amount of data, but the "next" write position may not be large enough.)
+// |MOJO_RESULT_BUSY| if there is already a two-phase write ongoing with
+// |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been
+// called, but not yet the matching |MojoEndWriteData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the
+// consumer is still open).
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+ void** buffer, // Out.
+ uint32_t* buffer_num_bytes, // In/out.
+ MojoWriteDataFlags flags);
+
+// Ends a two-phase write to the data pipe producer given by
+// |data_pipe_producer_handle| that was begun by a call to
+// |MojoBeginWriteData()| on the same handle. |num_bytes_written| should
+// indicate the amount of data actually written; it must be less than or equal
+// to the value of |*buffer_num_bytes| output by |MojoBeginWriteData()| and must
+// be a multiple of the element size. The buffer given by |*buffer| from
+// |MojoBeginWriteData()| must have been filled with exactly |num_bytes_written|
+// bytes of data.
+//
+// On failure, the two-phase write (if any) is ended (so the handle may become
+// writable again, if there's space available) but no data written to |*buffer|
+// is "put into" the data pipe.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_producer_handle| is not a handle to a data pipe producer or
+// |num_bytes_written| is invalid (greater than the maximum value provided
+// by |MojoBeginWriteData()| or not a multiple of the element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer is not in a
+// two-phase write (e.g., |MojoBeginWriteData()| was not called or
+// |MojoEndWriteData()| has already been called).
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+ uint32_t num_bytes_written);
+
+// Reads data from the data pipe consumer given by |data_pipe_consumer_handle|.
+// May also be used to discard data or query the amount of data available.
+//
+// If |flags| has neither |MOJO_READ_DATA_FLAG_DISCARD| nor
+// |MOJO_READ_DATA_FLAG_QUERY| set, this tries to read up to |*num_bytes| (which
+// must be a multiple of the data pipe's element size) bytes of data to
+// |elements| and set |*num_bytes| to the amount actually read. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, it will either read exactly
+// |*num_bytes| bytes of data or none.
+//
+// If flags has |MOJO_READ_DATA_FLAG_DISCARD| set, it discards up to
+// |*num_bytes| (which again be a multiple of the element size) bytes of data,
+// setting |*num_bytes| to the amount actually discarded. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE|, it will either discard exactly
+// |*num_bytes| bytes of data or none. In this case, |MOJO_READ_DATA_FLAG_QUERY|
+// must not be set, and |elements| is ignored (and should typically be set to
+// null).
+//
+// If flags has |MOJO_READ_DATA_FLAG_QUERY| set, it queries the amount of data
+// available, setting |*num_bytes| to the number of bytes available. In this
+// case, |MOJO_READ_DATA_FLAG_DISCARD| must not be set, and
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is ignored, as are |elements| and the input
+// value of |*num_bytes|.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success (see above for a description of the different
+// operations).
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_consumer_handle| is invalid, the combination of flags in
+// |flags| is invalid, etc.).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been
+// closed and data (or the required amount of data) was not available to
+// be read or discarded.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// set and the required amount of data is not available to be read or
+// discarded (and the producer is still open).
+// |MOJO_RESULT_BUSY| if there is a two-phase read ongoing with
+// |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been
+// called, but not yet the matching |MojoEndReadData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if there is no data to be read or discarded (and
+// the producer is still open) and |flags| does *not* have
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set.
+MOJO_SYSTEM_EXPORT MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+ void* elements, // Out.
+ uint32_t* num_bytes, // In/out.
+ MojoReadDataFlags flags);
+
+// Begins a two-phase read from the data pipe consumer given by
+// |data_pipe_consumer_handle|. On success, |*buffer| will be a pointer from
+// which the caller can read |*buffer_num_bytes| bytes of data. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, then the output value
+// |*buffer_num_bytes| will be at least as large as its input value, which must
+// also be a multiple of the element size (if |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// is not set, the input value of |*buffer_num_bytes| is ignored). |flags| must
+// not have |MOJO_READ_DATA_FLAG_DISCARD| or |MOJO_READ_DATA_FLAG_QUERY| set.
+//
+// During a two-phase read, |data_pipe_consumer_handle| is *not* readable.
+// E.g., if another thread tries to read from it, it will get
+// |MOJO_RESULT_BUSY|; that thread can then wait for |data_pipe_consumer_handle|
+// to become readable again.
+//
+// Once the caller has finished reading data from |*buffer|, it should call
+// |MojoEndReadData()| to specify the amount read and to complete the two-phase
+// read.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_consumer_handle| is not a handle to a data pipe consumer,
+// |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set and
+// |*buffer_num_bytes| is not a multiple of the element size, or |flags|
+// has invalid flags set).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been
+// closed.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// set and the required amount of data (specified by |*buffer_num_bytes|)
+// cannot be read from a contiguous buffer at this time. (Note that there
+// may be the required amount of data, but it may not be contiguous.)
+// |MOJO_RESULT_BUSY| if there is already a two-phase read ongoing with
+// |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been
+// called, but not yet the matching |MojoEndReadData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be read (and the
+// producer is still open).
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+ const void** buffer, // Out.
+ uint32_t* buffer_num_bytes, // In/out.
+ MojoReadDataFlags flags);
+
+// Ends a two-phase read from the data pipe consumer given by
+// |data_pipe_consumer_handle| that was begun by a call to |MojoBeginReadData()|
+// on the same handle. |num_bytes_read| should indicate the amount of data
+// actually read; it must be less than or equal to the value of
+// |*buffer_num_bytes| output by |MojoBeginReadData()| and must be a multiple of
+// the element size.
+//
+// On failure, the two-phase read (if any) is ended (so the handle may become
+// readable again) but no data is "removed" from the data pipe.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_consumer_handle| is not a handle to a data pipe consumer or
+// |num_bytes_written| is greater than the maximum value provided by
+// |MojoBeginReadData()| or not a multiple of the element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer is not in a
+// two-phase read (e.g., |MojoBeginReadData()| was not called or
+// |MojoEndReadData()| has already been called).
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_bytes_read);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
diff --git a/mojo/public/c/system/functions.h b/mojo/public/c/system/functions.h
new file mode 100644
index 0000000..6045f2f
--- /dev/null
+++ b/mojo/public/c/system/functions.h
@@ -0,0 +1,108 @@
+// 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 contains basic functions common to different Mojo system APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
+
+// Note: This header should be compilable as C.
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: Pointer parameters that are labelled "optional" may be null (at least
+// under some circumstances). Non-const pointer parameters are also labeled
+// "in", "out", or "in/out", to indicate how they are used. (Note that how/if
+// such a parameter is used may depend on other parameters or the requested
+// operation's success/failure. E.g., a separate |flags| parameter may control
+// whether a given "in/out" parameter is used for input, output, or both.)
+
+// Platform-dependent monotonically increasing tick count representing "right
+// now." The resolution of this clock is ~1-15ms. Resolution varies depending
+// on hardware/operating system configuration.
+MOJO_SYSTEM_EXPORT MojoTimeTicks MojoGetTimeTicksNow(void);
+
+// Closes the given |handle|.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle.
+//
+// Concurrent operations on |handle| may succeed (or fail as usual) if they
+// happen before the close, be cancelled with result |MOJO_RESULT_CANCELLED| if
+// they properly overlap (this is likely the case with |MojoWait()|, etc.), or
+// fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after.
+MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle);
+
+// Waits on the given handle until a signal indicated by |signals| is satisfied,
+// it becomes known that no signal indicated by |signals| will ever be satisfied
+// (see the description of the |MOJO_RESULT_CANCELLED| and
+// |MOJO_RESULT_FAILED_PRECONDITION| return values below), or until |deadline|
+// has passed.
+//
+// If |deadline| is |MOJO_DEADLINE_INDEFINITE|, this will wait "forever" (until
+// one of the other wait termination conditions is satisfied). If |deadline| is
+// 0, this will return |MOJO_RESULT_DEADLINE_EXCEEDED| only if one of the other
+// termination conditions (e.g., a signal is satisfied, or all signals are
+// unsatisfiable) is not already satisfied.
+//
+// Returns:
+// |MOJO_RESULT_OK| if some signal in |signals| was satisfied (or is already
+// satisfied).
+// |MOJO_RESULT_CANCELLED| if |handle| was closed (necessarily from another
+// thread) during the wait.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle (e.g., if
+// it has already been closed).
+// |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of
+// the signals being satisfied.
+// |MOJO_RESULT_FAILED_PRECONDITION| if it becomes known that none of the
+// signals in |signals| can ever be satisfied (e.g., when waiting on one
+// end of a message pipe and the other end is closed).
+//
+// If there are multiple waiters (on different threads, obviously) waiting on
+// the same handle and signal, and that signal becomes is satisfied, all waiters
+// will be awoken.
+MOJO_SYSTEM_EXPORT MojoResult MojoWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline);
+
+// Waits on |handles[0]|, ..., |handles[num_handles-1]| until (at least) one
+// satisfies a signal indicated in its respective |signals[0]|, ...,
+// |signals[num_handles-1]|, it becomes known that no signal in some
+// |signals[i]| will ever be satisfied, or until |deadline| has passed.
+//
+// This means that |MojoWaitMany()| behaves as if |MojoWait()| were called on
+// each handle/signals pair simultaneously, completing when the first
+// |MojoWait()| would complete.
+//
+// See |MojoWait()| for more details about |deadline|.
+//
+// Returns:
+// The index |i| (from 0 to |num_handles-1|) if |handle[i]| satisfies a signal
+// from |signals[i]|.
+// |MOJO_RESULT_CANCELLED| if some |handle[i]| was closed (necessarily from
+// another thread) during the wait.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some |handle[i]| is not a valid handle
+// (e.g., if it has already been closed).
+// |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of
+// handles satisfying any of its signals.
+// |MOJO_RESULT_FAILED_PRECONDITION| if it is or becomes impossible that SOME
+// |handle[i]| will ever satisfy any of the signals in |signals[i]|.
+MOJO_SYSTEM_EXPORT MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
diff --git a/mojo/public/c/system/macros.h b/mojo/public/c/system/macros.h
new file mode 100644
index 0000000..564ee60
--- /dev/null
+++ b/mojo/public/c/system/macros.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_C_SYSTEM_MACROS_H_
+
+#include <stddef.h>
+
+// Annotate a variable indicating it's okay if it's unused.
+// Use like:
+// int x MOJO_ALLOW_UNUSED = ...;
+#if defined(__GNUC__)
+#define MOJO_ALLOW_UNUSED __attribute__((unused))
+#else
+#define MOJO_ALLOW_UNUSED
+#endif
+
+// Annotate a function indicating that the caller must examine the return value.
+// Use like:
+// int foo() MOJO_WARN_UNUSED_RESULT;
+// Note that it can only be used on the prototype, and not the definition.
+#if defined(__GNUC__)
+#define MOJO_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define MOJO_WARN_UNUSED_RESULT
+#endif
+
+#ifdef __cplusplus
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+// scoped_ptr<MyType> my_var = ...;
+// if (TakeOwnership(my_var.get()) == SUCCESS)
+// mojo_ignore_result(my_var.release());
+//
+template <typename T>
+inline void mojo_ignore_result(const T&) {
+}
+#endif
+
+// Assert things at compile time. (|msg| should be a valid identifier name.)
+// This macro is currently C++-only, but we want to use it in the C core.h.
+// Use like:
+// MOJO_COMPILE_ASSERT(sizeof(Foo) == 12, Foo_has_invalid_size);
+#if __cplusplus >= 201103L
+#define MOJO_COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+#elif defined(__cplusplus)
+namespace mojo {
+template <bool>
+struct CompileAssert {};
+}
+#define MOJO_COMPILE_ASSERT(expr, msg) \
+ typedef ::mojo::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+#else
+#define MOJO_COMPILE_ASSERT(expr, msg)
+#endif
+
+// Like the C++11 |alignof| operator.
+#if __cplusplus >= 201103L
+#define MOJO_ALIGNOF(type) alignof(type)
+#elif defined(__GNUC__)
+#define MOJO_ALIGNOF(type) __alignof__(type)
+#elif defined(_MSC_VER)
+// The use of |sizeof| is to work around a bug in MSVC 2010 (see
+// http://goo.gl/isH0C; supposedly fixed since then).
+#define MOJO_ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#else
+#error "Please define MOJO_ALIGNOF() for your compiler."
+#endif
+
+// Specify the alignment of a |struct|, etc.
+// Use like:
+// struct MOJO_ALIGNAS(8) Foo { ... };
+// Unlike the C++11 |alignas()|, |alignment| must be an integer. It may not be a
+// type, nor can it be an expression like |MOJO_ALIGNOF(type)| (due to the
+// non-C++11 MSVS version).
+#if __cplusplus >= 201103L
+#define MOJO_ALIGNAS(alignment) alignas(alignment)
+#elif defined(__GNUC__)
+#define MOJO_ALIGNAS(alignment) __attribute__((aligned(alignment)))
+#elif defined(_MSC_VER)
+#define MOJO_ALIGNAS(alignment) __declspec(align(alignment))
+#else
+#error "Please define MOJO_ALIGNAS() for your compiler."
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_MACROS_H_
diff --git a/mojo/public/c/system/main.h b/mojo/public/c/system/main.h
new file mode 100644
index 0000000..31e2012
--- /dev/null
+++ b/mojo/public/c/system/main.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_C_SYSTEM_MAIN_H_
+#define MOJO_PUBLIC_C_SYSTEM_MAIN_H_
+
+#include "mojo/public/c/system/types.h"
+
+// Implement MojoMain directly as the entry point for an application.
+//
+// MojoResult MojoMain(MojoHandle service_provider_handle) {
+// ...
+// }
+//
+// TODO(davemoore): Establish this as part of our SDK for third party mojo
+// application writers.
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(WIN32)
+__declspec(dllexport) MojoResult
+ __cdecl MojoMain(MojoHandle service_provider_handle);
+#else // !defined(WIN32)
+__attribute__((visibility("default"))) MojoResult
+ MojoMain(MojoHandle service_provider_handle);
+#endif // defined(WIN32)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_MAIN_H_
diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h
new file mode 100644
index 0000000..b08ba75
--- /dev/null
+++ b/mojo/public/c/system/message_pipe.h
@@ -0,0 +1,179 @@
+// 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 contains types/constants and functions specific to message pipes.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateMessagePipeOptions|: Used to specify creation parameters for a
+// message pipe to |MojoCreateMessagePipe()|.
+// |uint32_t struct_size|: Set to the size of the
+// |MojoCreateMessagePipeOptions| struct. (Used to allow for future
+// extensions.)
+// |MojoCreateMessagePipeOptionsFlags flags|: Reserved for future use.
+// |MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode.
+
+typedef uint32_t MojoCreateMessagePipeOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateMessagePipeOptionsFlags
+ MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE \
+ ((MojoCreateMessagePipeOptionsFlags)0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateMessagePipeOptions {
+ uint32_t struct_size;
+ MojoCreateMessagePipeOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateMessagePipeOptions) == 8,
+ MojoCreateMessagePipeOptions_has_wrong_size);
+
+// |MojoWriteMessageFlags|: Used to specify different modes to
+// |MojoWriteMessage()|.
+// |MOJO_WRITE_MESSAGE_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoWriteMessageFlags;
+
+#ifdef __cplusplus
+const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE = 0;
+#else
+#define MOJO_WRITE_MESSAGE_FLAG_NONE ((MojoWriteMessageFlags)0)
+#endif
+
+// |MojoReadMessageFlags|: Used to specify different modes to
+// |MojoReadMessage()|.
+// |MOJO_READ_MESSAGE_FLAG_NONE| - No flags; default mode.
+// |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| - If the message is unable to be read
+// for whatever reason (e.g., the caller-supplied buffer is too small),
+// discard the message (i.e., simply dequeue it).
+
+typedef uint32_t MojoReadMessageFlags;
+
+#ifdef __cplusplus
+const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE = 0;
+const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD = 1 << 0;
+#else
+#define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags)0)
+#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags)1 << 0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a message pipe, which is a bidirectional communication channel for
+// framed data (i.e., messages). Messages can contain plain data and/or Mojo
+// handles.
+//
+// |options| may be set to null for a message pipe with the default options.
+//
+// On success, |*message_pipe_handle0| and |*message_pipe_handle1| are set to
+// handles for the two endpoints (ports) for the message pipe.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |*options| is invalid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+// been reached.
+//
+// TODO(vtl): Add an options struct pointer argument.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe(
+ const struct MojoCreateMessagePipeOptions* options, // Optional.
+ MojoHandle* message_pipe_handle0, // Out.
+ MojoHandle* message_pipe_handle1); // Out.
+
+// Writes a message to the message pipe endpoint given by |message_pipe_handle|,
+// with message data specified by |bytes| of size |num_bytes| and attached
+// handles specified by |handles| of count |num_handles|, and options specified
+// by |flags|. If there is no message data, |bytes| may be null, in which case
+// |num_bytes| must be zero. If there are no attached handles, |handles| may be
+// null, in which case |num_handles| must be zero.
+//
+// If handles are attached, on success the handles will no longer be valid (the
+// receiver will receive equivalent, but logically different, handles). Handles
+// to be sent should not be in simultaneous use (e.g., on another thread).
+//
+// Returns:
+// |MOJO_RESULT_OK| on success (i.e., the message was enqueued).
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |message_pipe_handle| is not a valid handle, or some of the
+// requirements above are not satisfied).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if some system limit has been reached, or
+// the number of handles to send is too large (TODO(vtl): reconsider the
+// latter case).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
+// Note that closing an endpoint is not necessarily synchronous (e.g.,
+// across processes), so this function may be succeed even if the other
+// endpoint has been closed (in which case the message would be dropped).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+// |MOJO_RESULT_BUSY| if some handle to be sent is currently in use.
+//
+// TODO(vtl): Add a notion of capacity for message pipes, and return
+// |MOJO_RESULT_SHOULD_WAIT| if the message pipe is full.
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoWriteMessage(MojoHandle message_pipe_handle,
+ const void* bytes, // Optional.
+ uint32_t num_bytes,
+ const MojoHandle* handles, // Optional.
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags);
+
+// Reads a message from the message pipe endpoint given by
+// |message_pipe_handle|; also usable to query the size of the next message or
+// discard the next message. |bytes|/|*num_bytes| indicate the buffer/buffer
+// size to receive the message data (if any) and |handles|/|*num_handles|
+// indicate the buffer/maximum handle count to receive the attached handles (if
+// any).
+//
+// |num_bytes| and |num_handles| are optional "in-out" parameters. If non-null,
+// on return |*num_bytes| and |*num_handles| will usually indicate the number
+// of bytes and number of attached handles in the "next" message, respectively,
+// whether that message was read or not. (If null, the number of bytes/handles
+// is treated as zero.)
+//
+// If |bytes| is null, then |*num_bytes| must be zero, and similarly for
+// |handles| and |*num_handles|.
+//
+// Partial reads are NEVER done. Either a full read is done and |MOJO_RESULT_OK|
+// returned, or the read is NOT done and |MOJO_RESULT_RESOURCE_EXHAUSTED| is
+// returned (if |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| was set, the message is
+// also discarded in this case).
+//
+// Returns:
+// |MOJO_RESULT_OK| on success (i.e., a message was actually read).
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid.
+// |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if one of the buffers to receive the
+// message/attached handles (|bytes|/|*num_bytes| or
+// |handles|/|*num_handles|) was too small. (TODO(vtl): Reconsider this
+// error code; should distinguish this from the hitting-system-limits
+// case.)
+// |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read.
+MOJO_SYSTEM_EXPORT MojoResult
+ MojoReadMessage(MojoHandle message_pipe_handle,
+ void* bytes, // Optional out.
+ uint32_t* num_bytes, // Optional in/out.
+ MojoHandle* handles, // Optional out.
+ uint32_t* num_handles, // Optional in/out.
+ MojoReadMessageFlags flags);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
diff --git a/mojo/public/c/system/system_export.h b/mojo/public/c/system/system_export.h
new file mode 100644
index 0000000..bc3b459
--- /dev/null
+++ b/mojo/public/c/system/system_export.h
@@ -0,0 +1,33 @@
+// 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_C_SYSTEM_SYSTEM_EXPORT_H_
+#define MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
+
+#if defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL)
+#if defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SYSTEM_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SYSTEM_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_SYSTEM_IMPL)
+
+#define MOJO_SYSTEM_EXPORT
+
+#endif // defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL)
+
+#endif // MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/mojo/public/c/system/tests/BUILD.gn b/mojo/public/c/system/tests/BUILD.gn
new file mode 100644
index 0000000..e9aa06f
--- /dev/null
+++ b/mojo/public/c/system/tests/BUILD.gn
@@ -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.
+
+source_set("tests") {
+ testonly = true
+ visibility = [ "//mojo/public/cpp/system/tests:mojo_public_system_unittests" ]
+
+ deps = [
+ "//mojo/public/c/environment",
+ "//mojo/public/c/system",
+ "//testing/gtest",
+ ]
+
+ sources = [
+ "core_unittest.cc",
+ "core_unittest_pure_c.c",
+ "macros_unittest.cc",
+ ]
+}
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_system_perftests
+executable("perftests") {
+ testonly = true
+ output_name = "mojo_public_system_perftests"
+
+ sources = [
+ "core_perftest.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/common/test:run_all_perftests",
+ "//mojo/public/c/environment",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//mojo/public/cpp/utility",
+ "//testing/gtest",
+ ]
+}
diff --git a/mojo/public/c/system/tests/core_perftest.cc b/mojo/public/c/system/tests/core_perftest.cc
new file mode 100644
index 0000000..b9a8114
--- /dev/null
+++ b/mojo/public/c/system/tests/core_perftest.cc
@@ -0,0 +1,339 @@
+// 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.
+
+// This tests the performance of the C API.
+
+#include "mojo/public/c/system/core.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TODO(vtl): (here and below) crbug.com/342893
+#if !defined(WIN32)
+#include <time.h>
+#include "mojo/public/cpp/utility/thread.h"
+#endif // !defined(WIN32)
+
+namespace {
+
+#if !defined(WIN32)
+class MessagePipeWriterThread : public mojo::Thread {
+ public:
+ MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes)
+ : handle_(handle), num_bytes_(num_bytes), num_writes_(0) {}
+ virtual ~MessagePipeWriterThread() {}
+
+ virtual void Run() override {
+ char buffer[10000];
+ assert(num_bytes_ <= sizeof(buffer));
+
+ // TODO(vtl): Should I throttle somehow?
+ for (;;) {
+ MojoResult result = MojoWriteMessage(
+ handle_, buffer, num_bytes_, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ if (result == MOJO_RESULT_OK) {
+ num_writes_++;
+ continue;
+ }
+
+ // We failed to write.
+ // Either |handle_| or its peer was closed.
+ assert(result == MOJO_RESULT_INVALID_ARGUMENT ||
+ result == MOJO_RESULT_FAILED_PRECONDITION);
+ break;
+ }
+ }
+
+ // Use only after joining the thread.
+ int64_t num_writes() const { return num_writes_; }
+
+ private:
+ const MojoHandle handle_;
+ const uint32_t num_bytes_;
+ int64_t num_writes_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeWriterThread);
+};
+
+class MessagePipeReaderThread : public mojo::Thread {
+ public:
+ explicit MessagePipeReaderThread(MojoHandle handle)
+ : handle_(handle), num_reads_(0) {}
+ virtual ~MessagePipeReaderThread() {}
+
+ virtual void Run() override {
+ char buffer[10000];
+
+ for (;;) {
+ uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ MojoResult result = MojoReadMessage(
+ handle_, buffer, &num_bytes, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE);
+ if (result == MOJO_RESULT_OK) {
+ num_reads_++;
+ continue;
+ }
+
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ result = MojoWait(
+ handle_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+ if (result == MOJO_RESULT_OK) {
+ // Go to the top of the loop to read again.
+ continue;
+ }
+ }
+
+ // We failed to read and possibly failed to wait.
+ // Either |handle_| or its peer was closed.
+ assert(result == MOJO_RESULT_INVALID_ARGUMENT ||
+ result == MOJO_RESULT_FAILED_PRECONDITION);
+ break;
+ }
+ }
+
+ // Use only after joining the thread.
+ int64_t num_reads() const { return num_reads_; }
+
+ private:
+ const MojoHandle handle_;
+ int64_t num_reads_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessagePipeReaderThread);
+};
+#endif // !defined(WIN32)
+
+class CorePerftest : public testing::Test {
+ public:
+ CorePerftest() : buffer_(NULL), num_bytes_(0) {}
+ virtual ~CorePerftest() {}
+
+ static void NoOp(void* /*closure*/) {}
+
+ static void MessagePipe_CreateAndClose(void* closure) {
+ CorePerftest* self = static_cast<CorePerftest*>(closure);
+ MojoResult result MOJO_ALLOW_UNUSED;
+ result = MojoCreateMessagePipe(NULL, &self->h0_, &self->h1_);
+ assert(result == MOJO_RESULT_OK);
+ result = MojoClose(self->h0_);
+ assert(result == MOJO_RESULT_OK);
+ result = MojoClose(self->h1_);
+ assert(result == MOJO_RESULT_OK);
+ }
+
+ static void MessagePipe_WriteAndRead(void* closure) {
+ CorePerftest* self = static_cast<CorePerftest*>(closure);
+ MojoResult result MOJO_ALLOW_UNUSED;
+ result = MojoWriteMessage(self->h0_,
+ self->buffer_,
+ self->num_bytes_,
+ NULL,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+ assert(result == MOJO_RESULT_OK);
+ uint32_t read_bytes = self->num_bytes_;
+ result = MojoReadMessage(self->h1_,
+ self->buffer_,
+ &read_bytes,
+ NULL,
+ NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ assert(result == MOJO_RESULT_OK);
+ }
+
+ static void MessagePipe_EmptyRead(void* closure) {
+ CorePerftest* self = static_cast<CorePerftest*>(closure);
+ MojoResult result MOJO_ALLOW_UNUSED;
+ result = MojoReadMessage(
+ self->h0_, NULL, NULL, NULL, NULL, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ assert(result == MOJO_RESULT_SHOULD_WAIT);
+ }
+
+ protected:
+#if !defined(WIN32)
+ void DoMessagePipeThreadedTest(unsigned num_writers,
+ unsigned num_readers,
+ uint32_t num_bytes) {
+ static const int64_t kPerftestTimeMicroseconds = 3 * 1000000;
+
+ assert(num_writers > 0);
+ assert(num_readers > 0);
+
+ MojoResult result MOJO_ALLOW_UNUSED;
+ result = MojoCreateMessagePipe(NULL, &h0_, &h1_);
+ assert(result == MOJO_RESULT_OK);
+
+ std::vector<MessagePipeWriterThread*> writers;
+ for (unsigned i = 0; i < num_writers; i++)
+ writers.push_back(new MessagePipeWriterThread(h0_, num_bytes));
+
+ std::vector<MessagePipeReaderThread*> readers;
+ for (unsigned i = 0; i < num_readers; i++)
+ readers.push_back(new MessagePipeReaderThread(h1_));
+
+ // Start time here, just before we fire off the threads.
+ const MojoTimeTicks start_time = MojoGetTimeTicksNow();
+
+ // Interleave the starts.
+ for (unsigned i = 0; i < num_writers || i < num_readers; i++) {
+ if (i < num_writers)
+ writers[i]->Start();
+ if (i < num_readers)
+ readers[i]->Start();
+ }
+
+ Sleep(kPerftestTimeMicroseconds);
+
+ // Close both handles to make writers and readers stop immediately.
+ result = MojoClose(h0_);
+ assert(result == MOJO_RESULT_OK);
+ result = MojoClose(h1_);
+ assert(result == MOJO_RESULT_OK);
+
+ // Join everything.
+ for (unsigned i = 0; i < num_writers; i++)
+ writers[i]->Join();
+ for (unsigned i = 0; i < num_readers; i++)
+ readers[i]->Join();
+
+ // Stop time here.
+ MojoTimeTicks end_time = MojoGetTimeTicksNow();
+
+ // Add up write and read counts, and destroy the threads.
+ int64_t num_writes = 0;
+ for (unsigned i = 0; i < num_writers; i++) {
+ num_writes += writers[i]->num_writes();
+ delete writers[i];
+ }
+ writers.clear();
+ int64_t num_reads = 0;
+ for (unsigned i = 0; i < num_readers; i++) {
+ num_reads += readers[i]->num_reads();
+ delete readers[i];
+ }
+ readers.clear();
+
+ char test_name[200];
+ sprintf(test_name,
+ "MessagePipe_Threaded_Writes_%uw_%ur_%ubytes",
+ num_writers,
+ num_readers,
+ static_cast<unsigned>(num_bytes));
+ mojo::test::LogPerfResult(
+ test_name,
+ 1000000.0 * static_cast<double>(num_writes) / (end_time - start_time),
+ "writes/second");
+ sprintf(test_name,
+ "MessagePipe_Threaded_Reads_%uw_%ur_%ubytes",
+ num_writers,
+ num_readers,
+ static_cast<unsigned>(num_bytes));
+ mojo::test::LogPerfResult(
+ test_name,
+ 1000000.0 * static_cast<double>(num_reads) / (end_time - start_time),
+ "reads/second");
+ }
+#endif // !defined(WIN32)
+
+ MojoHandle h0_;
+ MojoHandle h1_;
+
+ void* buffer_;
+ uint32_t num_bytes_;
+
+ private:
+#if !defined(WIN32)
+ void Sleep(int64_t microseconds) {
+ struct timespec req = {
+ static_cast<time_t>(microseconds / 1000000), // Seconds.
+ static_cast<long>(microseconds % 1000000) * 1000L // Nanoseconds.
+ };
+ int rv MOJO_ALLOW_UNUSED;
+ rv = nanosleep(&req, NULL);
+ assert(rv == 0);
+ }
+#endif // !defined(WIN32)
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(CorePerftest);
+};
+
+// A no-op test so we can compare performance.
+TEST_F(CorePerftest, NoOp) {
+ mojo::test::IterateAndReportPerf("NoOp", &CorePerftest::NoOp, this);
+}
+
+TEST_F(CorePerftest, MessagePipe_CreateAndClose) {
+ mojo::test::IterateAndReportPerf("MessagePipe_CreateAndClose",
+ &CorePerftest::MessagePipe_CreateAndClose,
+ this);
+}
+
+TEST_F(CorePerftest, MessagePipe_WriteAndRead) {
+ MojoResult result MOJO_ALLOW_UNUSED;
+ result = MojoCreateMessagePipe(NULL, &h0_, &h1_);
+ assert(result == MOJO_RESULT_OK);
+ char buffer[10000] = {0};
+ buffer_ = buffer;
+ num_bytes_ = 10u;
+ mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10bytes",
+ &CorePerftest::MessagePipe_WriteAndRead,
+ this);
+ num_bytes_ = 100u;
+ mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_100bytes",
+ &CorePerftest::MessagePipe_WriteAndRead,
+ this);
+ num_bytes_ = 1000u;
+ mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_1000bytes",
+ &CorePerftest::MessagePipe_WriteAndRead,
+ this);
+ num_bytes_ = 10000u;
+ mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10000bytes",
+ &CorePerftest::MessagePipe_WriteAndRead,
+ this);
+ result = MojoClose(h0_);
+ assert(result == MOJO_RESULT_OK);
+ result = MojoClose(h1_);
+ assert(result == MOJO_RESULT_OK);
+}
+
+TEST_F(CorePerftest, MessagePipe_EmptyRead) {
+ MojoResult result MOJO_ALLOW_UNUSED;
+ result = MojoCreateMessagePipe(NULL, &h0_, &h1_);
+ assert(result == MOJO_RESULT_OK);
+ mojo::test::IterateAndReportPerf(
+ "MessagePipe_EmptyRead", &CorePerftest::MessagePipe_EmptyRead, this);
+ result = MojoClose(h0_);
+ assert(result == MOJO_RESULT_OK);
+ result = MojoClose(h1_);
+ assert(result == MOJO_RESULT_OK);
+}
+
+#if !defined(WIN32)
+TEST_F(CorePerftest, MessagePipe_Threaded) {
+ DoMessagePipeThreadedTest(1u, 1u, 100u);
+ DoMessagePipeThreadedTest(2u, 2u, 100u);
+ DoMessagePipeThreadedTest(3u, 3u, 100u);
+ DoMessagePipeThreadedTest(10u, 10u, 100u);
+ DoMessagePipeThreadedTest(10u, 1u, 100u);
+ DoMessagePipeThreadedTest(1u, 10u, 100u);
+
+ // For comparison of overhead:
+ DoMessagePipeThreadedTest(1u, 1u, 10u);
+ // 100 was done above.
+ DoMessagePipeThreadedTest(1u, 1u, 1000u);
+ DoMessagePipeThreadedTest(1u, 1u, 10000u);
+
+ DoMessagePipeThreadedTest(3u, 3u, 10u);
+ // 100 was done above.
+ DoMessagePipeThreadedTest(3u, 3u, 1000u);
+ DoMessagePipeThreadedTest(3u, 3u, 10000u);
+}
+#endif // !defined(WIN32)
+
+} // namespace
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
new file mode 100644
index 0000000..d071f48
--- /dev/null
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -0,0 +1,298 @@
+// 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.
+
+// This file tests the C API.
+
+#include "mojo/public/c/system/core.h"
+
+#include <string.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(CoreTest, GetTimeTicksNow) {
+ const MojoTimeTicks start = MojoGetTimeTicksNow();
+ EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+ << "MojoGetTimeTicksNow should return nonzero value";
+}
+
+// The only handle that's guaranteed to be invalid is |MOJO_HANDLE_INVALID|.
+// Tests that everything that takes a handle properly recognizes it.
+TEST(CoreTest, InvalidHandle) {
+ MojoHandle h0, h1;
+ MojoHandleSignals sig;
+ char buffer[10] = {0};
+ uint32_t buffer_size;
+ void* write_pointer;
+ const void* read_pointer;
+
+ // Close:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID));
+
+ // Wait:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000));
+ h0 = MOJO_HANDLE_INVALID;
+ sig = ~MOJO_HANDLE_SIGNAL_NONE;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE));
+
+ // Message pipe:
+ EXPECT_EQ(
+ MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWriteMessage(h0, buffer, 3, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(
+ MOJO_RESULT_INVALID_ARGUMENT,
+ MojoReadMessage(
+ h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Data pipe:
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWriteData(h0, buffer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE));
+ write_pointer = NULL;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoBeginWriteData(
+ h0, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndWriteData(h0, 1));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoReadData(h0, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+ read_pointer = NULL;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoBeginReadData(
+ h0, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndReadData(h0, 1));
+
+ // Shared buffer:
+ h1 = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoDuplicateBufferHandle(h0, NULL, &h1));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoMapBuffer(h0, 0, 1, &write_pointer, MOJO_MAP_BUFFER_FLAG_NONE));
+}
+
+TEST(CoreTest, BasicMessagePipe) {
+ MojoHandle h0, h1;
+ MojoHandleSignals sig;
+ char buffer[10] = {0};
+ uint32_t buffer_size;
+
+ h0 = MOJO_HANDLE_INVALID;
+ h1 = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &h0, &h1));
+ EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+ EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+ // Shouldn't be readable.
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // Should be writable.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+ // Try to read.
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(
+ MOJO_RESULT_SHOULD_WAIT,
+ MojoReadMessage(
+ h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Write to |h1|.
+ static const char kHello[] = "hello";
+ buffer_size = static_cast<uint32_t>(sizeof(kHello));
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ MojoWriteMessage(
+ h1, kHello, buffer_size, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // |h0| should be readable.
+ sig = MOJO_HANDLE_SIGNAL_READABLE;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE));
+
+ // Read from |h0|.
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ MojoReadMessage(
+ h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(kHello)), buffer_size);
+ EXPECT_STREQ(kHello, buffer);
+
+ // |h0| should no longer be readable.
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 10));
+
+ // Close |h0|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+
+ // |h1| should no longer be readable or writable.
+ EXPECT_EQ(
+ MOJO_RESULT_FAILED_PRECONDITION,
+ MojoWait(
+ h1, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, 1000));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+}
+
+// TODO(ncbray): enable these tests once NaCl supports the corresponding APIs.
+#ifdef __native_client__
+#define MAYBE_BasicDataPipe DISABLED_BasicDataPipe
+#define MAYBE_BasicSharedBuffer DISABLED_BasicSharedBuffer
+#else
+#define MAYBE_BasicDataPipe BasicDataPipe
+#define MAYBE_BasicSharedBuffer BasicSharedBuffer
+#endif
+
+TEST(CoreTest, MAYBE_BasicDataPipe) {
+ MojoHandle hp, hc;
+ MojoHandleSignals sig;
+ char buffer[20] = {0};
+ uint32_t buffer_size;
+ void* write_pointer;
+ const void* read_pointer;
+
+ hp = MOJO_HANDLE_INVALID;
+ hc = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(NULL, &hp, &hc));
+ EXPECT_NE(hp, MOJO_HANDLE_INVALID);
+ EXPECT_NE(hc, MOJO_HANDLE_INVALID);
+
+ // The consumer |hc| shouldn't be readable.
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // The producer |hp| should be writable.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWait(hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+ // Try to read from |hc|.
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+
+ // Try to begin a two-phase read from |hc|.
+ read_pointer = NULL;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ MojoBeginReadData(
+ hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+
+ // Write to |hp|.
+ static const char kHello[] = "hello ";
+ // Don't include terminating null.
+ buffer_size = static_cast<uint32_t>(strlen(kHello));
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ MojoWriteData(hp, kHello, &buffer_size, MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // |hc| should be(come) readable.
+ sig = MOJO_HANDLE_SIGNAL_READABLE;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWaitMany(&hc, &sig, 1, MOJO_DEADLINE_INDEFINITE));
+
+ // Do a two-phase write to |hp|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoBeginWriteData(
+ hp, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE));
+ static const char kWorld[] = "world";
+ ASSERT_GE(buffer_size, sizeof(kWorld));
+ // Include the terminating null.
+ memcpy(write_pointer, kWorld, sizeof(kWorld));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoEndWriteData(hp, static_cast<uint32_t>(sizeof(kWorld))));
+
+ // Read one character from |hc|.
+ memset(buffer, 0, sizeof(buffer));
+ buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadData(hc, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+
+ // Close |hp|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hp));
+
+ // |hc| should still be readable.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // Do a two-phase read from |hc|.
+ read_pointer = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoBeginReadData(
+ hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE));
+ ASSERT_LE(buffer_size, sizeof(buffer) - 1);
+ memcpy(&buffer[1], read_pointer, buffer_size);
+ EXPECT_EQ(MOJO_RESULT_OK, MojoEndReadData(hc, buffer_size));
+ EXPECT_STREQ("hello world", buffer);
+
+ // |hc| should no longer be readable.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 1000));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(hc));
+
+ // TODO(vtl): Test the other way around -- closing the consumer should make
+ // the producer never-writable?
+}
+
+TEST(CoreTest, MAYBE_BasicSharedBuffer) {
+ MojoHandle h0, h1;
+ void* pointer;
+
+ // Create a shared buffer (|h0|).
+ h0 = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(NULL, 100, &h0));
+ EXPECT_NE(h0, MOJO_HANDLE_INVALID);
+
+ // Map everything.
+ pointer = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoMapBuffer(h0, 0, 100, &pointer, MOJO_MAP_BUFFER_FLAG_NONE));
+ ASSERT_TRUE(pointer);
+ static_cast<char*>(pointer)[50] = 'x';
+
+ // Duplicate |h0| to |h1|.
+ h1 = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(h0, NULL, &h1));
+ EXPECT_NE(h1, MOJO_HANDLE_INVALID);
+
+ // Close |h0|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+
+ // The mapping should still be good.
+ static_cast<char*>(pointer)[51] = 'y';
+
+ // Unmap it.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
+
+ // Map half of |h1|.
+ pointer = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoMapBuffer(h1, 50, 50, &pointer, MOJO_MAP_BUFFER_FLAG_NONE));
+ ASSERT_TRUE(pointer);
+
+ // It should have what we wrote.
+ EXPECT_EQ('x', static_cast<char*>(pointer)[0]);
+ EXPECT_EQ('y', static_cast<char*>(pointer)[1]);
+
+ // Unmap it.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+}
+
+// Defined in core_unittest_pure_c.c.
+extern "C" const char* MinimalCTest(void);
+
+// This checks that things actually work in C (not C++).
+TEST(CoreTest, MinimalCTest) {
+ const char* failure = MinimalCTest();
+ EXPECT_TRUE(failure == NULL) << failure;
+}
+
+// TODO(vtl): Add multi-threaded tests.
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
new file mode 100644
index 0000000..33de688
--- /dev/null
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -0,0 +1,96 @@
+// 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.
+
+#ifdef __cplusplus
+#error "This file should be compiled as C, not C++."
+#endif
+
+#include <stddef.h>
+#include <string.h>
+
+// Include all the header files that are meant to be compilable as C. Start with
+// core.h, since it's the most important one.
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/c/system/macros.h"
+
+// The joys of the C preprocessor....
+#define STRINGIFY(x) #x
+#define STRINGIFY2(x) STRINGIFY(x)
+#define FAILURE(message) \
+ __FILE__ "(" STRINGIFY2(__LINE__) "): Failure: " message
+
+// Poor man's gtest.
+#define EXPECT_EQ(a, b) \
+ do { \
+ if ((a) != (b)) \
+ return FAILURE(STRINGIFY(a) " != " STRINGIFY(b) " (expected ==)"); \
+ } while (0)
+#define EXPECT_NE(a, b) \
+ do { \
+ if ((a) == (b)) \
+ return FAILURE(STRINGIFY(a) " == " STRINGIFY(b) " (expected !=)"); \
+ } while (0)
+
+// This function exists mainly to be compiled and linked. We do some cursory
+// checks and call it from a unit test, to make sure that link problems aren't
+// missed due to deadstripping. Returns null on success and a string on failure
+// (describing the failure).
+const char* MinimalCTest(void) {
+ // MSVS before 2013 *really* only supports C90: All variables must be declared
+ // at the top. (MSVS 2013 is more reasonable.)
+ MojoTimeTicks ticks;
+ MojoHandle handle0, handle1;
+ MojoHandleSignals signals;
+ const char kHello[] = "hello";
+ char buffer[200] = {0};
+ uint32_t num_bytes;
+
+ ticks = MojoGetTimeTicksNow();
+ EXPECT_NE(ticks, 0);
+
+ handle0 = MOJO_HANDLE_INVALID;
+ EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0));
+
+ EXPECT_EQ(
+ MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE));
+
+ handle1 = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1));
+
+ signals = MOJO_HANDLE_SIGNAL_READABLE;
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ MojoWaitMany(&handle0, &signals, 1, 1));
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(handle0,
+ kHello,
+ (uint32_t)sizeof(kHello),
+ NULL,
+ 0u,
+ MOJO_WRITE_DATA_FLAG_NONE));
+
+ EXPECT_EQ(
+ MOJO_RESULT_OK,
+ MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE));
+
+ num_bytes = (uint32_t)sizeof(buffer);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(handle1,
+ buffer,
+ &num_bytes,
+ NULL,
+ NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ((uint32_t)sizeof(kHello), num_bytes);
+ EXPECT_EQ(0, memcmp(buffer, kHello, sizeof(kHello)));
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle0));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle1));
+
+ // TODO(vtl): data pipe
+
+ return NULL;
+}
diff --git a/mojo/public/c/system/tests/macros_unittest.cc b/mojo/public/c/system/tests/macros_unittest.cc
new file mode 100644
index 0000000..6a694b8
--- /dev/null
+++ b/mojo/public/c/system/tests/macros_unittest.cc
@@ -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.
+
+// 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): Fix no-compile tests (which are all disabled; crbug.com/105388)
+// and write some "negative" tests.
+
+#include "mojo/public/c/system/macros.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+TEST(MacrosTest, AllowUnused) {
+ // Test that no warning/error is issued even though |x| is unused.
+ int x MOJO_ALLOW_UNUSED = 123;
+}
+
+int MustUseReturnedResult() MOJO_WARN_UNUSED_RESULT;
+int MustUseReturnedResult() {
+ return 456;
+}
+
+TEST(MacrosTest, WarnUnusedResult) {
+ if (!MustUseReturnedResult())
+ abort();
+}
+
+// First test |MOJO_COMPILE_ASSERT()| in a global scope.
+MOJO_COMPILE_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t),
+ bad_compile_assert_failure_in_global_scope);
+
+TEST(MacrosTest, CompileAssert) {
+ // Then in a local scope.
+ MOJO_COMPILE_ASSERT(sizeof(int32_t) == 2 * sizeof(int16_t),
+ bad_compile_assert_failure);
+}
+
+TEST(MacrosTest, Alignof) {
+ // Strictly speaking, this isn't a portable test, but I think it'll pass on
+ // all the platforms we currently support.
+ EXPECT_EQ(1u, MOJO_ALIGNOF(char));
+ EXPECT_EQ(4u, MOJO_ALIGNOF(int32_t));
+ EXPECT_EQ(8u, MOJO_ALIGNOF(int64_t));
+ EXPECT_EQ(8u, MOJO_ALIGNOF(double));
+}
+
+// These structs are used in the Alignas test. Define them globally to avoid
+// MSVS warnings/errors.
+#if defined(_MSC_VER)
+#pragma warning(push)
+// Disable the warning "structure was padded due to __declspec(align())".
+#pragma warning(disable : 4324)
+#endif
+struct MOJO_ALIGNAS(1) StructAlignas1 {
+ char x;
+};
+struct MOJO_ALIGNAS(4) StructAlignas4 {
+ char x;
+};
+struct MOJO_ALIGNAS(8) StructAlignas8 {
+ char x;
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+TEST(MacrosTest, Alignas) {
+ EXPECT_EQ(1u, MOJO_ALIGNOF(StructAlignas1));
+ EXPECT_EQ(4u, MOJO_ALIGNOF(StructAlignas4));
+ EXPECT_EQ(8u, MOJO_ALIGNOF(StructAlignas8));
+}
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h
new file mode 100644
index 0000000..5a72d2f
--- /dev/null
+++ b/mojo/public/c/system/types.h
@@ -0,0 +1,176 @@
+// 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 contains types and constants/macros common to different Mojo system
+// APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_TYPES_H_
+#define MOJO_PUBLIC_C_SYSTEM_TYPES_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/system/macros.h"
+
+// TODO(vtl): Notes: Use of undefined flags will lead to undefined behavior
+// (typically they'll be ignored), not necessarily an error.
+
+// |MojoTimeTicks|: Used to specify time ticks. Value is in microseconds.
+
+typedef int64_t MojoTimeTicks;
+
+// |MojoHandle|: Handles to Mojo objects.
+// |MOJO_HANDLE_INVALID| - A value that is never a valid handle.
+
+typedef uint32_t MojoHandle;
+
+#ifdef __cplusplus
+const MojoHandle MOJO_HANDLE_INVALID = 0;
+#else
+#define MOJO_HANDLE_INVALID ((MojoHandle)0)
+#endif
+
+// |MojoResult|: Result codes for Mojo operations. Non-negative values are
+// success codes; negative values are error/failure codes.
+// |MOJO_RESULT_OK| - Not an error; returned on success. Note that positive
+// |MojoResult|s may also be used to indicate success.
+// |MOJO_RESULT_CANCELLED| - Operation was cancelled, typically by the caller.
+// |MOJO_RESULT_UNKNOWN| - Unknown error (e.g., if not enough information is
+// available for a more specific error).
+// |MOJO_RESULT_INVALID_ARGUMENT| - Caller specified an invalid argument. This
+// differs from |MOJO_RESULT_FAILED_PRECONDITION| in that the former
+// indicates arguments that are invalid regardless of the state of the
+// system.
+// |MOJO_RESULT_DEADLINE_EXCEEDED| - Deadline expired before the operation
+// could complete.
+// |MOJO_RESULT_NOT_FOUND| - Some requested entity was not found (i.e., does
+// not exist).
+// |MOJO_RESULT_ALREADY_EXISTS| - Some entity or condition that we attempted
+// to create already exists.
+// |MOJO_RESULT_PERMISSION_DENIED| - The caller does not have permission to
+// for the operation (use |MOJO_RESULT_RESOURCE_EXHAUSTED| for rejections
+// caused by exhausting some resource instead).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| - Some resource required for the call
+// (possibly some quota) has been exhausted.
+// |MOJO_RESULT_FAILED_PRECONDITION| - The system is not in a state required
+// for the operation (use this if the caller must do something to rectify
+// the state before retrying).
+// |MOJO_RESULT_ABORTED| - The operation was aborted by the system, possibly
+// due to a concurrency issue (use this if the caller may retry at a
+// higher level).
+// |MOJO_RESULT_OUT_OF_RANGE| - The operation was attempted past the valid
+// range. Unlike |MOJO_RESULT_INVALID_ARGUMENT|, this indicates that the
+// operation may be/become valid depending on the system state. (This
+// error is similar to |MOJO_RESULT_FAILED_PRECONDITION|, but is more
+// specific.)
+// |MOJO_RESULT_UNIMPLEMENTED| - The operation is not implemented, supported,
+// or enabled.
+// |MOJO_RESULT_INTERNAL| - Internal error: this should never happen and
+// indicates that some invariant expected by the system has been broken.
+// |MOJO_RESULT_UNAVAILABLE| - The operation is (temporarily) currently
+// unavailable. The caller may simply retry the operation (possibly with a
+// backoff).
+// |MOJO_RESULT_DATA_LOSS| - Unrecoverable data loss or corruption.
+// |MOJO_RESULT_BUSY| - One of the resources involved is currently being used
+// (possibly on another thread) in a way that prevents the current
+// operation from proceeding, e.g., if the other operation may result in
+// the resource being invalidated.
+// |MOJO_RESULT_SHOULD_WAIT| - The request cannot currently be completed
+// (e.g., if the data requested is not yet available). The caller should
+// wait for it to be feasible using |MojoWait()| or |MojoWaitMany()|.
+//
+// Note that positive values are also available as success codes.
+//
+// The codes from |MOJO_RESULT_OK| to |MOJO_RESULT_DATA_LOSS| come from
+// Google3's canonical error codes.
+//
+// TODO(vtl): Add a |MOJO_RESULT_UNSATISFIABLE|?
+
+typedef int32_t MojoResult;
+
+#ifdef __cplusplus
+const MojoResult MOJO_RESULT_OK = 0;
+const MojoResult MOJO_RESULT_CANCELLED = -1;
+const MojoResult MOJO_RESULT_UNKNOWN = -2;
+const MojoResult MOJO_RESULT_INVALID_ARGUMENT = -3;
+const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED = -4;
+const MojoResult MOJO_RESULT_NOT_FOUND = -5;
+const MojoResult MOJO_RESULT_ALREADY_EXISTS = -6;
+const MojoResult MOJO_RESULT_PERMISSION_DENIED = -7;
+const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED = -8;
+const MojoResult MOJO_RESULT_FAILED_PRECONDITION = -9;
+const MojoResult MOJO_RESULT_ABORTED = -10;
+const MojoResult MOJO_RESULT_OUT_OF_RANGE = -11;
+const MojoResult MOJO_RESULT_UNIMPLEMENTED = -12;
+const MojoResult MOJO_RESULT_INTERNAL = -13;
+const MojoResult MOJO_RESULT_UNAVAILABLE = -14;
+const MojoResult MOJO_RESULT_DATA_LOSS = -15;
+const MojoResult MOJO_RESULT_BUSY = -16;
+const MojoResult MOJO_RESULT_SHOULD_WAIT = -17;
+#else
+#define MOJO_RESULT_OK ((MojoResult)0)
+#define MOJO_RESULT_CANCELLED ((MojoResult) - 1)
+#define MOJO_RESULT_UNKNOWN ((MojoResult) - 2)
+#define MOJO_RESULT_INVALID_ARGUMENT ((MojoResult) - 3)
+#define MOJO_RESULT_DEADLINE_EXCEEDED ((MojoResult) - 4)
+#define MOJO_RESULT_NOT_FOUND ((MojoResult) - 5)
+#define MOJO_RESULT_ALREADY_EXISTS ((MojoResult) - 6)
+#define MOJO_RESULT_PERMISSION_DENIED ((MojoResult) - 7)
+#define MOJO_RESULT_RESOURCE_EXHAUSTED ((MojoResult) - 8)
+#define MOJO_RESULT_FAILED_PRECONDITION ((MojoResult) - 9)
+#define MOJO_RESULT_ABORTED ((MojoResult) - 10)
+#define MOJO_RESULT_OUT_OF_RANGE ((MojoResult) - 11)
+#define MOJO_RESULT_UNIMPLEMENTED ((MojoResult) - 12)
+#define MOJO_RESULT_INTERNAL ((MojoResult) - 13)
+#define MOJO_RESULT_UNAVAILABLE ((MojoResult) - 14)
+#define MOJO_RESULT_DATA_LOSS ((MojoResult) - 15)
+#define MOJO_RESULT_BUSY ((MojoResult) - 16)
+#define MOJO_RESULT_SHOULD_WAIT ((MojoResult) - 17)
+#endif
+
+// |MojoDeadline|: Used to specify deadlines (timeouts), in microseconds (except
+// for |MOJO_DEADLINE_INDEFINITE|).
+// |MOJO_DEADLINE_INDEFINITE| - Used to indicate "forever".
+
+typedef uint64_t MojoDeadline;
+
+#ifdef __cplusplus
+const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast<MojoDeadline>(-1);
+#else
+#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline) - 1)
+#endif
+
+// |MojoHandleSignals|: Used to specify signals that can be waited on for a
+// handle (and which can be triggered), e.g., the ability to read or write to
+// the handle.
+// |MOJO_HANDLE_SIGNAL_NONE| - No flags. |MojoWait()|, etc. will return
+// |MOJO_RESULT_FAILED_PRECONDITION| if you attempt to wait on this.
+// |MOJO_HANDLE_SIGNAL_READABLE| - Can read (e.g., a message) from the handle.
+// |MOJO_HANDLE_SIGNAL_WRITABLE| - Can write (e.g., a message) to the handle.
+
+typedef uint32_t MojoHandleSignals;
+
+#ifdef __cplusplus
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE = 0;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE = 1 << 0;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1;
+#else
+#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0)
+#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0)
+#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals)1 << 1)
+#endif
+
+// TODO(vtl): Add out parameters with this to MojoWait/MojoWaitMany.
+// Note: This struct is not extensible (and only has 32-bit quantities), so it's
+// 32-bit-aligned.
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int32_t) == 4, int32_t_has_weird_alignment);
+struct MOJO_ALIGNAS(4) MojoHandleSignalsState {
+ MojoHandleSignals satisfied_signals;
+ MojoHandleSignals satisfiable_signals;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoHandleSignalsState) == 8,
+ MojoHandleSignalsState_has_wrong_size);
+
+#endif // MOJO_PUBLIC_C_SYSTEM_TYPES_H_
diff --git a/mojo/public/c/test_support/BUILD.gn b/mojo/public/c/test_support/BUILD.gn
new file mode 100644
index 0000000..42db434
--- /dev/null
+++ b/mojo/public/c/test_support/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_test_support
+shared_library("test_support") {
+ output_name = "mojo_test_support"
+
+ defines = [ "MOJO_TEST_SUPPORT_IMPLEMENTATION" ]
+
+ sources = [
+ "test_support.h",
+ "test_support_export.h",
+ # TODO(vtl): Convert this to thunks http://crbug.com/386799
+ "../../tests/test_support_private.cc",
+ "../../tests/test_support_private.h",
+ ]
+
+ if (is_mac) {
+# TODO(GYP)
+# # Make it a run-path dependent library.
+# 'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+ }
+}
diff --git a/mojo/public/c/test_support/test_support.h b/mojo/public/c/test_support/test_support.h
new file mode 100644
index 0000000..2b686b2
--- /dev/null
+++ b/mojo/public/c/test_support/test_support.h
@@ -0,0 +1,48 @@
+// 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_C_TEST_SUPPORT_TEST_SUPPORT_H_
+#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdio.h>
+
+#include "mojo/public/c/test_support/test_support_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOJO_TEST_SUPPORT_EXPORT void MojoTestSupportLogPerfResult(
+ const char* test_name,
+ double value,
+ const char* units);
+
+// Opens a "/"-delimited file path relative to the source root.
+MOJO_TEST_SUPPORT_EXPORT FILE* MojoTestSupportOpenSourceRootRelativeFile(
+ const char* source_root_relative_path);
+
+// Enumerates a "/"-delimited directory path relative to the source root.
+// Returns only regular files. The return value is a heap-allocated array of
+// heap-allocated strings. Each must be free'd separately.
+//
+// The return value is built like so:
+//
+// char** rv = (char**) calloc(N + 1, sizeof(char*));
+// rv[0] = strdup("a");
+// rv[1] = strdup("b");
+// rv[2] = strdup("c");
+// ...
+// rv[N] = NULL;
+//
+MOJO_TEST_SUPPORT_EXPORT
+char** MojoTestSupportEnumerateSourceRootRelativeDirectory(
+ const char* source_root_relative_path);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
diff --git a/mojo/public/c/test_support/test_support_export.h b/mojo/public/c/test_support/test_support_export.h
new file mode 100644
index 0000000..e22a9e3
--- /dev/null
+++ b/mojo/public/c/test_support/test_support_export.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
+#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
+
+#if defined(WIN32)
+
+#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION)
+#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllexport)
+#else
+#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION)
+#define MOJO_TEST_SUPPORT_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_TEST_SUPPORT_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#endif // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
diff --git a/mojo/public/cpp/DEPS b/mojo/public/cpp/DEPS
new file mode 100644
index 0000000..74acd7c
--- /dev/null
+++ b/mojo/public/cpp/DEPS
@@ -0,0 +1,18 @@
+include_rules = [
+ # Require explicit dependencies in each directory.
+ "-mojo/public",
+ # But everyone can depend on the C and C++ system headers.
+ "+mojo/public/c/system",
+ "+mojo/public/cpp/system",
+ # Ditto for the C environment headers (but not the C++ environment, since it
+ # has dependencies of its own).
+ "+mojo/public/c/environment",
+]
+
+specific_include_rules = {
+ r".*_(unit|perf)test\.cc": [
+ "+testing",
+ "+mojo/public/cpp/test_support",
+ "+mojo/public/cpp/utility",
+ ],
+}
diff --git a/mojo/public/cpp/README.md b/mojo/public/cpp/README.md
new file mode 100644
index 0000000..8f03d98
--- /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 in
+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/application/BUILD.gn b/mojo/public/cpp/application/BUILD.gn
new file mode 100644
index 0000000..900edef
--- /dev/null
+++ b/mojo/public/cpp/application/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.
+
+# GYP version: mojo/mojo_public.gypi:mojo_application_base
+source_set("application") {
+ sources = [
+ "application_connection.h",
+ "application_delegate.h",
+ "application_impl.h",
+ "connect.h",
+ "service_provider_impl.h",
+ "interface_factory.h",
+ "interface_factory_impl.h",
+ "lib/application_connection.cc",
+ "lib/application_delegate.cc",
+ "lib/application_impl.cc",
+ "lib/service_provider_impl.cc",
+ "lib/service_connector.cc",
+ "lib/service_connector.h",
+ "lib/service_registry.cc",
+ "lib/service_registry.h",
+ "lib/weak_service_provider.cc",
+ "lib/weak_service_provider.h",
+ ]
+
+ deps = [
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/environment",
+ "//mojo/public/cpp/system",
+ "//mojo/public/interfaces/application",
+ ]
+}
+
+# GYP version: mojo/mojo_public.gypi:mojo_application_standalone
+source_set("standalone") {
+ sources = [
+ "lib/application_runner.cc"
+ ]
+
+ public_deps = [ ":application" ]
+
+ deps = [
+ "//mojo/public/cpp/environment:standalone",
+ "//mojo/public/cpp/utility",
+ ]
+}
diff --git a/mojo/public/cpp/application/DEPS b/mojo/public/cpp/application/DEPS
new file mode 100644
index 0000000..dcd8e6f
--- /dev/null
+++ b/mojo/public/cpp/application/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+ "+mojo/public/cpp/bindings",
+ "+mojo/public/cpp/environment",
+ "+mojo/public/interfaces/application",
+ "+mojo/public/interfaces/service_provider",
+]
+
+specific_include_rules = {
+ r"application_runner_chromium.*": [
+ "+base",
+ "+mojo/common",
+ "+mojo/public/cpp"
+ ],
+ r"application_runner.*": [
+ "+mojo/public/cpp"
+ ],
+}
diff --git a/mojo/public/cpp/application/application_connection.h b/mojo/public/cpp/application/application_connection.h
new file mode 100644
index 0000000..d5d6dde
--- /dev/null
+++ b/mojo/public/cpp/application/application_connection.h
@@ -0,0 +1,82 @@
+// 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_APPLICATION_APPLICATION_CONNECTION_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_CONNECTION_H_
+
+#include <string>
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+// An instance of this class is passed to
+// ApplicationDelegate's ConfigureIncomingConnection() method each time a
+// connection is made to this app, and to ApplicationDelegate's
+// ConfigureOutgoingConnection() method when the app connects to
+// another.
+//
+// To use define a class that implements your specific service api, e.g. FooImpl
+// to implement a service named Foo.
+// That class must subclass an InterfaceImpl specialization.
+//
+// Then implement an InterfaceFactory<Foo> that binds instances of FooImpl to
+// InterfaceRequest<Foo>s and register that on the connection.
+//
+// connection->AddService(&factory);
+//
+// Or if you have multiple factories implemented by the same type, explicitly
+// specify the interface to register the factory for:
+//
+// connection->AddService<Foo>(&my_foo_and_bar_factory_);
+// connection->AddService<Bar>(&my_foo_and_bar_factory_);
+//
+// The InterfaceFactory must outlive the ApplicationConnection.
+class ApplicationConnection {
+ public:
+ virtual ~ApplicationConnection();
+
+ template <typename Interface>
+ void AddService(InterfaceFactory<Interface>* factory) {
+ AddServiceConnector(
+ new internal::InterfaceFactoryConnector<Interface>(factory));
+ }
+
+ // Connect to the service implementing |Interface|.
+ template <typename Interface>
+ void ConnectToService(InterfacePtr<Interface>* ptr) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ GetServiceProvider()->ConnectToService(Interface::Name_,
+ pipe.handle1.Pass());
+ }
+
+ // The url identifying the application on the other end of this connection.
+ virtual const std::string& GetRemoteApplicationURL() = 0;
+
+ // Establishes a new connection to an application.
+ // TODO(davemoore): Would it be better to expose the ApplicationImpl?
+ virtual ApplicationConnection* ConnectToApplication(
+ const std::string& url) = 0;
+
+ // Connect to application identified by |application_url| and connect to
+ // the service implementation of the interface identified by |Interface|.
+ template <typename Interface>
+ void ConnectToService(const std::string& application_url,
+ InterfacePtr<Interface>* ptr) {
+ ConnectToApplication(application_url)->ConnectToService(ptr);
+ }
+
+ // Raw ServiceProvider interface to remote application.
+ virtual ServiceProvider* GetServiceProvider() = 0;
+
+ private:
+ virtual void AddServiceConnector(
+ internal::ServiceConnectorBase* service_connector) = 0;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_APPLICATION_CONNECTION_H_
diff --git a/mojo/public/cpp/application/application_delegate.h b/mojo/public/cpp/application/application_delegate.h
new file mode 100644
index 0000000..cb67308
--- /dev/null
+++ b/mojo/public/cpp/application/application_delegate.h
@@ -0,0 +1,44 @@
+// 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_APPLICATION_APPLICATION_DELEGATE_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_DELEGATE_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+class ApplicationImpl;
+
+class ApplicationDelegate {
+ public:
+ ApplicationDelegate();
+ virtual ~ApplicationDelegate();
+
+ // Implement this method to create the specific subclass of
+ // ApplicationDelegate. Ownership is taken by the caller. It will be deleted.
+ static ApplicationDelegate* Create();
+
+ virtual void Initialize(ApplicationImpl* app);
+
+ // Override this method to configure what services a connection supports when
+ // being connected to from an app.
+ // return false to reject the connection entirely.
+ virtual bool ConfigureIncomingConnection(ApplicationConnection* connection);
+
+ // Override this method to configure what services a connection supports when
+ // connecting to another app.
+ // return false to reject the connection entirely.
+ virtual bool ConfigureOutgoingConnection(ApplicationConnection* connection);
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationDelegate);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_APPLICATION_DELEGATE_H_
diff --git a/mojo/public/cpp/application/application_impl.h b/mojo/public/cpp/application/application_impl.h
new file mode 100644
index 0000000..4b4cb93
--- /dev/null
+++ b/mojo/public/cpp/application/application_impl.h
@@ -0,0 +1,110 @@
+// 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_APPLICATION_APPLICATION_IMPL_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_IMPL_H_
+#include <vector>
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/cpp/application/lib/service_registry.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+
+namespace mojo {
+
+class ApplicationDelegate;
+
+// Utility class for communicating with the Shell, and providing Services
+// to clients.
+//
+// To use define a class that implements your specific server api, e.g. FooImpl
+// to implement a service named Foo.
+// That class must subclass an InterfaceImpl specialization.
+//
+// If there is context that is to be shared amongst all instances, define a
+// constructor with that class as its only argument, otherwise define an empty
+// constructor.
+//
+// class FooImpl : public InterfaceImpl<Foo> {
+// public:
+// FooImpl(ApplicationContext* app_context) {}
+// };
+//
+// or
+//
+// class BarImpl : public InterfaceImpl<Bar> {
+// public:
+// // contexts will remain valid for the lifetime of BarImpl.
+// BarImpl(ApplicationContext* app_context, BarContext* service_context)
+// : app_context_(app_context), servicecontext_(context) {}
+//
+// Create an ApplicationImpl instance that collects any service implementations.
+//
+// ApplicationImpl app(service_provider_handle);
+// app.AddService<FooImpl>();
+//
+// BarContext context;
+// app.AddService<BarImpl>(&context);
+//
+//
+class ApplicationImpl : public InterfaceImpl<Application> {
+ public:
+ ApplicationImpl(ApplicationDelegate* delegate,
+ ScopedMessagePipeHandle shell_handle);
+ ApplicationImpl(ApplicationDelegate* delegate,
+ MojoHandle shell_handle);
+ virtual ~ApplicationImpl();
+
+ Shell* shell() const { return shell_.get(); }
+
+ // Returns any initial configuration arguments, passed by the Shell.
+ const Array<String>& args() { return args_; }
+
+ // Establishes a new connection to an application. Caller does not own.
+ ApplicationConnection* ConnectToApplication(const String& application_url);
+
+ // Connect to application identified by |application_url| and connect to the
+ // service implementation of the interface identified by |Interface|.
+ template <typename Interface>
+ void ConnectToService(const std::string& application_url,
+ InterfacePtr<Interface>* ptr) {
+ ConnectToApplication(application_url)->ConnectToService(ptr);
+ }
+
+ private:
+ class ShellPtrWatcher;
+
+ void BindShell(ScopedMessagePipeHandle shell_handle);
+ void ClearConnections();
+ void OnShellError() {
+ ClearConnections();
+ Terminate();
+ };
+
+ // Quits the main run loop for this application.
+ static void Terminate();
+
+ // Application implementation.
+ virtual void Initialize(Array<String> args) override;
+ virtual void AcceptConnection(const String& requestor_url,
+ ServiceProviderPtr provider) override;
+
+ typedef std::vector<internal::ServiceRegistry*> ServiceRegistryList;
+
+ bool initialized_;
+ ServiceRegistryList incoming_service_registries_;
+ ServiceRegistryList outgoing_service_registries_;
+ ApplicationDelegate* delegate_;
+ ShellPtr shell_;
+ ShellPtrWatcher* shell_watch_;
+ Array<String> args_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_APPLICATION_IMPL_H_
diff --git a/mojo/public/cpp/application/application_runner.h b/mojo/public/cpp/application/application_runner.h
new file mode 100644
index 0000000..b88ec8e
--- /dev/null
+++ b/mojo/public/cpp/application/application_runner.h
@@ -0,0 +1,44 @@
+// 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_APPLICATION_APPLICATION_RUNNER_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_RUNNER_H_
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class ApplicationDelegate;
+
+// A utility for running an Application. The typical use case is to use
+// when writing your MojoMain:
+//
+// MojoResult MojoMain(MojoHandle shell_handle) {
+// mojo::ApplicationRunner runner(new MyApplicationDelegate());
+// return runner.Run(shell_handle);
+// }
+//
+// ApplicationRunner takes care of mojo environment initialization and
+// shutdown, and starting a RunLoop from which your application can run and
+// ultimately Quit().
+class ApplicationRunner {
+ public:
+ // Takes ownership of |delegate|.
+ explicit ApplicationRunner(ApplicationDelegate* delegate);
+ ~ApplicationRunner();
+
+ // Once the various parameters have been set above, use Run to initialize an
+ // ApplicationImpl wired to the provided delegate, and run a RunLoop until
+ // the application exits.
+ MojoResult Run(MojoHandle shell_handle);
+
+ private:
+ ApplicationDelegate* delegate_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationRunner);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_APPLICATION_RUNNER_H_
diff --git a/mojo/public/cpp/application/connect.h b/mojo/public/cpp/application/connect.h
new file mode 100644
index 0000000..a41c028
--- /dev/null
+++ b/mojo/public/cpp/application/connect.h
@@ -0,0 +1,22 @@
+// 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_APPLICATION_CONNECT_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+template <typename Interface>
+inline void ConnectToService(ServiceProvider* service_provider,
+ InterfacePtr<Interface>* ptr) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ service_provider->ConnectToService(Interface::Name_, pipe.handle1.Pass());
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
diff --git a/mojo/public/cpp/application/interface_factory.h b/mojo/public/cpp/application/interface_factory.h
new file mode 100644
index 0000000..90abd13
--- /dev/null
+++ b/mojo/public/cpp/application/interface_factory.h
@@ -0,0 +1,30 @@
+// 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_APPLICATION_INTERFACE_FACTORY_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_H_
+
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace mojo {
+
+class ApplicationConnection;
+template <typename Interface> class InterfaceRequest;
+
+// Implement this class to provide implementations of a given interface and
+// bind them to incoming requests. The implementation of this class is
+// responsible for managing the lifetime of the implementations of the
+// interface.
+template <typename Interface>
+class InterfaceFactory {
+ public:
+ virtual ~InterfaceFactory() {}
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<Interface> request) = 0;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_H_
diff --git a/mojo/public/cpp/application/interface_factory_impl.h b/mojo/public/cpp/application/interface_factory_impl.h
new file mode 100644
index 0000000..72d3254
--- /dev/null
+++ b/mojo/public/cpp/application/interface_factory_impl.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_APPLICATION_INTERFACE_FACTORY_IMPL_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_IMPL_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+
+namespace mojo {
+
+// Use this class to allocate and bind instances of Impl to interface requests.
+// The lifetime of the constructed Impl is bound to the pipe.
+template <typename Impl,
+ typename Interface = typename Impl::ImplementedInterface>
+class InterfaceFactoryImpl : public InterfaceFactory<Interface> {
+ public:
+ virtual ~InterfaceFactoryImpl() {}
+
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<Interface> request) override {
+ BindToRequest(new Impl(), &request);
+ }
+};
+
+// Use this class to allocate and bind instances of Impl constructed with a
+// context parameter to interface requests. The lifetime of the constructed
+// Impl is bound to the pipe.
+template <typename Impl,
+ typename Context,
+ typename Interface = typename Impl::ImplementedInterface>
+class InterfaceFactoryImplWithContext : public InterfaceFactory<Interface> {
+ public:
+ explicit InterfaceFactoryImplWithContext(Context* context)
+ : context_(context) {}
+ virtual ~InterfaceFactoryImplWithContext() {}
+
+ virtual void Create(ApplicationConnection* connection,
+ InterfaceRequest<Interface> request) override {
+ BindToRequest(new Impl(context_), &request);
+ }
+
+ private:
+ Context* context_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_INTERFACE_FACTORY_IMPL_H_
diff --git a/mojo/public/cpp/application/lazy_interface_ptr.h b/mojo/public/cpp/application/lazy_interface_ptr.h
new file mode 100644
index 0000000..1f4b973
--- /dev/null
+++ b/mojo/public/cpp/application/lazy_interface_ptr.h
@@ -0,0 +1,46 @@
+// 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_APPLICATION_LAZY_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LAZY_INTERFACE_PTR_H_
+
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+template<typename Interface>
+class LazyInterfacePtr : public InterfacePtr<Interface> {
+ public:
+ LazyInterfacePtr() : service_provider_(nullptr) {}
+
+ LazyInterfacePtr(ServiceProvider* service_provider)
+ : service_provider_(service_provider) {
+ }
+
+ void set_service_provider(ServiceProvider* service_provider) {
+ if (service_provider != service_provider_) {
+ InterfacePtr<Interface>::reset();
+ }
+ service_provider_ = service_provider;
+ }
+
+ Interface* get() const {
+ if (!InterfacePtr<Interface>::get()) {
+ mojo::ConnectToService<Interface>(
+ service_provider_,
+ const_cast<LazyInterfacePtr<Interface>*>(this));
+ }
+ return InterfacePtr<Interface>::get();
+ }
+ Interface* operator->() const { return get(); }
+ Interface& operator*() const { return *get(); }
+
+ private:
+ ServiceProvider* service_provider_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_LAZY_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/application/lib/application_connection.cc b/mojo/public/cpp/application/lib/application_connection.cc
new file mode 100644
index 0000000..4978a35
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_connection.cc
@@ -0,0 +1,12 @@
+// 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/application/application_connection.h"
+
+namespace mojo {
+
+ApplicationConnection::~ApplicationConnection() {}
+
+} // namespace mojo
+
diff --git a/mojo/public/cpp/application/lib/application_delegate.cc b/mojo/public/cpp/application/lib/application_delegate.cc
new file mode 100644
index 0000000..715daa0
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_delegate.cc
@@ -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.
+
+#include "mojo/public/cpp/application/application_delegate.h"
+
+namespace mojo {
+
+ApplicationDelegate::ApplicationDelegate() {}
+ApplicationDelegate::~ApplicationDelegate() {}
+
+void ApplicationDelegate::Initialize(ApplicationImpl* app) {}
+
+bool ApplicationDelegate::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ return true;
+}
+
+bool ApplicationDelegate::ConfigureOutgoingConnection(
+ ApplicationConnection* connection) {
+ return true;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/application_impl.cc b/mojo/public/cpp/application/lib/application_impl.cc
new file mode 100644
index 0000000..0c86356
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_impl.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/application/application_impl.h"
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/lib/service_registry.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+class ApplicationImpl::ShellPtrWatcher : public ErrorHandler {
+ public:
+ ShellPtrWatcher(ApplicationImpl* impl)
+ : impl_(impl) {}
+
+ virtual ~ShellPtrWatcher() {}
+
+ virtual void OnConnectionError() override { impl_->OnShellError(); }
+
+ private:
+ ApplicationImpl* impl_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ShellPtrWatcher);
+};
+
+ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate,
+ ScopedMessagePipeHandle shell_handle)
+ : initialized_(false), delegate_(delegate), shell_watch_(nullptr) {
+ BindShell(shell_handle.Pass());
+}
+
+ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate,
+ MojoHandle shell_handle)
+ : initialized_(false), delegate_(delegate), shell_watch_(nullptr) {
+ BindShell(MakeScopedHandle(MessagePipeHandle(shell_handle)));
+}
+
+void ApplicationImpl::ClearConnections() {
+ for (ServiceRegistryList::iterator i(incoming_service_registries_.begin());
+ i != incoming_service_registries_.end(); ++i)
+ delete *i;
+ for (ServiceRegistryList::iterator i(outgoing_service_registries_.begin());
+ i != outgoing_service_registries_.end(); ++i)
+ delete *i;
+ incoming_service_registries_.clear();
+ outgoing_service_registries_.clear();
+}
+
+ApplicationImpl::~ApplicationImpl() {
+ ClearConnections();
+ delete shell_watch_;
+}
+
+void ApplicationImpl::Initialize(Array<String> args) {
+ MOJO_CHECK(!initialized_);
+ initialized_ = true;
+ args_ = args.Pass();
+ delegate_->Initialize(this);
+}
+
+ApplicationConnection* ApplicationImpl::ConnectToApplication(
+ const String& application_url) {
+ MOJO_CHECK(initialized_);
+ ServiceProviderPtr out_service_provider;
+ shell_->ConnectToApplication(application_url, Get(&out_service_provider));
+ internal::ServiceRegistry* registry = new internal::ServiceRegistry(
+ this,
+ application_url,
+ out_service_provider.Pass());
+ if (!delegate_->ConfigureOutgoingConnection(registry)) {
+ delete registry;
+ return nullptr;
+ }
+ outgoing_service_registries_.push_back(registry);
+ return registry;
+}
+
+void ApplicationImpl::BindShell(ScopedMessagePipeHandle shell_handle) {
+ shell_watch_ = new ShellPtrWatcher(this);
+ shell_.Bind(shell_handle.Pass());
+ shell_.set_client(this);
+ shell_.set_error_handler(shell_watch_);
+}
+
+void ApplicationImpl::AcceptConnection(const String& requestor_url,
+ ServiceProviderPtr service_provider) {
+ internal::ServiceRegistry* registry = new internal::ServiceRegistry(
+ this, requestor_url, service_provider.Pass());
+ if (!delegate_->ConfigureIncomingConnection(registry)) {
+ delete registry;
+ return;
+ }
+ incoming_service_registries_.push_back(registry);
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/application_runner.cc b/mojo/public/cpp/application/lib/application_runner.cc
new file mode 100644
index 0000000..b451a90
--- /dev/null
+++ b/mojo/public/cpp/application/lib/application_runner.cc
@@ -0,0 +1,36 @@
+// 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/application/application_runner.h"
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+// static
+void ApplicationImpl::Terminate() {
+ RunLoop::current()->Quit();
+}
+
+ApplicationRunner::ApplicationRunner(ApplicationDelegate* delegate)
+ : delegate_(delegate) {}
+ApplicationRunner::~ApplicationRunner() { assert(!delegate_); }
+
+MojoResult ApplicationRunner::Run(MojoHandle shell_handle) {
+ Environment env;
+ {
+ RunLoop loop;
+ ApplicationImpl app(delegate_, shell_handle);
+ loop.Run();
+ }
+
+ delete delegate_;
+ delegate_ = nullptr;
+ return MOJO_RESULT_OK;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_connector.cc b/mojo/public/cpp/application/lib/service_connector.cc
new file mode 100644
index 0000000..51e7d84
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_connector.cc
@@ -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.
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+
+namespace mojo {
+namespace internal {
+
+ServiceConnectorBase::ServiceConnectorBase(const std::string& name)
+ : name_(name),
+ application_connection_(nullptr) {
+}
+
+ServiceConnectorBase::~ServiceConnectorBase() {}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_connector.h b/mojo/public/cpp/application/lib/service_connector.h
new file mode 100644
index 0000000..9ddaeb7
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_connector.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_APPLICATION_LIB_SERVICE_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+namespace internal {
+
+class ServiceConnectorBase {
+ public:
+ ServiceConnectorBase(const std::string& name);
+ virtual ~ServiceConnectorBase();
+ virtual void ConnectToService(const std::string& name,
+ ScopedMessagePipeHandle client_handle) = 0;
+ std::string name() const { return name_; }
+ void set_application_connection(ApplicationConnection* connection) {
+ application_connection_ = connection; }
+
+ protected:
+ std::string name_;
+ ApplicationConnection* application_connection_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnectorBase);
+};
+
+template <typename Interface>
+class InterfaceFactoryConnector : public ServiceConnectorBase {
+ public:
+ explicit InterfaceFactoryConnector(InterfaceFactory<Interface>* factory)
+ : ServiceConnectorBase(Interface::Name_), factory_(factory) {}
+ virtual ~InterfaceFactoryConnector() {}
+
+ virtual void ConnectToService(const std::string& name,
+ ScopedMessagePipeHandle client_handle) {
+ factory_->Create(application_connection_,
+ MakeRequest<Interface>(client_handle.Pass()));
+ }
+
+ private:
+ InterfaceFactory<Interface>* factory_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceFactoryConnector);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
diff --git a/mojo/public/cpp/application/lib/service_provider_impl.cc b/mojo/public/cpp/application/lib/service_provider_impl.cc
new file mode 100644
index 0000000..08a0648
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_provider_impl.cc
@@ -0,0 +1,69 @@
+// 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/application/service_provider_impl.h"
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/cpp/application/lib/weak_service_provider.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+
+ServiceProviderImpl::ServiceProviderImpl() : remote_(nullptr) {
+}
+
+ServiceProviderImpl::~ServiceProviderImpl() {
+}
+
+ServiceProvider* ServiceProviderImpl::CreateRemoteServiceProvider() {
+ // TODO(beng): it sure would be nice if this method could return a scoped_ptr.
+ MOJO_DCHECK(!remote_);
+ remote_ = new internal::WeakServiceProvider(this, client());
+ return remote_;
+}
+
+void ServiceProviderImpl::ConnectToService(
+ const String& service_name,
+ ScopedMessagePipeHandle client_handle) {
+ if (service_connectors_.find(service_name) == service_connectors_.end()) {
+ client_handle.reset();
+ return;
+ }
+
+ internal::ServiceConnectorBase* service_connector =
+ service_connectors_[service_name];
+ return service_connector->ConnectToService(service_name,
+ client_handle.Pass());
+}
+
+void ServiceProviderImpl::OnConnectionError() {
+ ClearRemote();
+}
+
+void ServiceProviderImpl::AddServiceConnector(
+ internal::ServiceConnectorBase* service_connector) {
+ RemoveServiceConnector(service_connector);
+ service_connectors_[service_connector->name()] = service_connector;
+ // TODO(beng): perhaps take app connection thru ctor??
+ service_connector->set_application_connection(nullptr);
+}
+
+void ServiceProviderImpl::RemoveServiceConnector(
+ internal::ServiceConnectorBase* service_connector) {
+ NameToServiceConnectorMap::iterator it =
+ service_connectors_.find(service_connector->name());
+ if (it == service_connectors_.end())
+ return;
+ delete it->second;
+ service_connectors_.erase(it);
+}
+
+void ServiceProviderImpl::ClearRemote() {
+ if (remote_) {
+ remote_->Clear();
+ remote_ = nullptr;
+ }
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_registry.cc b/mojo/public/cpp/application/lib/service_registry.cc
new file mode 100644
index 0000000..c83e84f
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_registry.cc
@@ -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.
+
+#include "mojo/public/cpp/application/lib/service_registry.h"
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/lib/service_connector.h"
+
+namespace mojo {
+namespace internal {
+
+ServiceRegistry::ServiceRegistry(ApplicationImpl* application_impl,
+ const std::string& url,
+ ServiceProviderPtr service_provider)
+ : application_impl_(application_impl),
+ url_(url),
+ remote_service_provider_(service_provider.Pass()) {
+ remote_service_provider_.set_client(this);
+}
+
+ServiceRegistry::ServiceRegistry() : application_impl_(nullptr) {}
+
+ServiceRegistry::~ServiceRegistry() {
+ for (NameToServiceConnectorMap::iterator i =
+ name_to_service_connector_.begin();
+ i != name_to_service_connector_.end(); ++i) {
+ delete i->second;
+ }
+ name_to_service_connector_.clear();
+}
+
+void ServiceRegistry::AddServiceConnector(
+ ServiceConnectorBase* service_connector) {
+ RemoveServiceConnectorInternal(service_connector);
+ name_to_service_connector_[service_connector->name()] = service_connector;
+ service_connector->set_application_connection(this);
+}
+
+void ServiceRegistry::RemoveServiceConnector(
+ ServiceConnectorBase* service_connector) {
+ RemoveServiceConnectorInternal(service_connector);
+ if (name_to_service_connector_.empty())
+ remote_service_provider_.reset();
+}
+
+bool ServiceRegistry::RemoveServiceConnectorInternal(
+ ServiceConnectorBase* service_connector) {
+ NameToServiceConnectorMap::iterator it =
+ name_to_service_connector_.find(service_connector->name());
+ if (it == name_to_service_connector_.end())
+ return false;
+ delete it->second;
+ name_to_service_connector_.erase(it);
+ return true;
+}
+
+const std::string& ServiceRegistry::GetRemoteApplicationURL() {
+ return url_;
+}
+
+ServiceProvider* ServiceRegistry::GetServiceProvider() {
+ return remote_service_provider_.get();
+}
+
+ApplicationConnection* ServiceRegistry::ConnectToApplication(
+ const std::string& url) {
+ return application_impl_->ConnectToApplication(url);
+}
+
+void ServiceRegistry::ConnectToService(const mojo::String& service_name,
+ ScopedMessagePipeHandle client_handle) {
+ if (name_to_service_connector_.find(service_name) ==
+ name_to_service_connector_.end()) {
+ client_handle.reset();
+ return;
+ }
+ internal::ServiceConnectorBase* service_connector =
+ name_to_service_connector_[service_name];
+ return service_connector->ConnectToService(service_name,
+ client_handle.Pass());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/service_registry.h b/mojo/public/cpp/application/lib/service_registry.h
new file mode 100644
index 0000000..32d4b2b
--- /dev/null
+++ b/mojo/public/cpp/application/lib/service_registry.h
@@ -0,0 +1,65 @@
+// 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_APPLICATION_LIB_SERVICE_REGISTRY_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
+
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+
+class Application;
+class ApplicationImpl;
+
+namespace internal {
+
+class ServiceConnectorBase;
+
+// A ServiceRegistry represents each half of a connection between two
+// applications, allowing customization of which services are published to the
+// other.
+class ServiceRegistry : public ServiceProvider, public ApplicationConnection {
+ public:
+ ServiceRegistry();
+ ServiceRegistry(ApplicationImpl* application_impl,
+ const std::string& url,
+ ServiceProviderPtr service_provider);
+ virtual ~ServiceRegistry();
+
+ // ApplicationConnection overrides.
+ virtual void AddServiceConnector(
+ ServiceConnectorBase* service_connector) override;
+ virtual const std::string& GetRemoteApplicationURL() override;
+ virtual ApplicationConnection* ConnectToApplication(
+ const std::string& url) override;
+ virtual ServiceProvider* GetServiceProvider() override;
+
+ virtual void RemoveServiceConnector(ServiceConnectorBase* service_connector);
+
+ private:
+ // ServiceProvider method.
+ virtual void ConnectToService(const mojo::String& service_name,
+ ScopedMessagePipeHandle client_handle) override;
+
+ ApplicationImpl* application_impl_;
+ const std::string url_;
+
+ private:
+ bool RemoveServiceConnectorInternal(
+ ServiceConnectorBase* service_connector);
+
+ Application* application_;
+ typedef std::map<std::string, ServiceConnectorBase*>
+ NameToServiceConnectorMap;
+ NameToServiceConnectorMap name_to_service_connector_;
+ ServiceProviderPtr remote_service_provider_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceRegistry);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
diff --git a/mojo/public/cpp/application/lib/weak_service_provider.cc b/mojo/public/cpp/application/lib/weak_service_provider.cc
new file mode 100644
index 0000000..b0d2511
--- /dev/null
+++ b/mojo/public/cpp/application/lib/weak_service_provider.cc
@@ -0,0 +1,36 @@
+// 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/application/lib/weak_service_provider.h"
+
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+WeakServiceProvider::WeakServiceProvider(ServiceProviderImpl* creator,
+ ServiceProvider* service_provider)
+ : creator_(creator),
+ service_provider_(service_provider) {}
+
+WeakServiceProvider::~WeakServiceProvider() {
+ if (creator_)
+ creator_->ClearRemote();
+}
+
+void WeakServiceProvider::Clear() {
+ creator_ = nullptr;
+ service_provider_ = nullptr;
+}
+
+void WeakServiceProvider::ConnectToService(
+ const String& service_name,
+ ScopedMessagePipeHandle client_handle) {
+ if (service_provider_)
+ service_provider_->ConnectToService(service_name, client_handle.Pass());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/application/lib/weak_service_provider.h b/mojo/public/cpp/application/lib/weak_service_provider.h
new file mode 100644
index 0000000..05ce939
--- /dev/null
+++ b/mojo/public/cpp/application/lib/weak_service_provider.h
@@ -0,0 +1,41 @@
+// 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_APPLICATION_LIB_WEAK_SERVICE_PROVIDER_H_
+#define MOJO_PUBLIC_APPLICATION_LIB_WEAK_SERVICE_PROVIDER_H_
+
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+class ServiceProviderImpl;
+namespace internal {
+class ServiceConnectorBase;
+
+// Implements a weak pointer to a ServiceProvider. Necessary as the lifetime of
+// the ServiceProviderImpl is bound to that of its pipe, but code may continue
+// to reference a remote service provider beyond the lifetime of said pipe.
+// Calls to ConnectToService() are silently dropped when the pipe is closed.
+class WeakServiceProvider : public ServiceProvider {
+ public:
+ WeakServiceProvider(ServiceProviderImpl* creator,
+ ServiceProvider* service_provider);
+ virtual ~WeakServiceProvider();
+
+ void Clear();
+
+ private:
+ // Overridden from ServiceProvider:
+ virtual void ConnectToService(const String& service_name,
+ ScopedMessagePipeHandle client_handle) override;
+
+ ServiceProviderImpl* creator_;
+ ServiceProvider* service_provider_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(WeakServiceProvider);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_LIB_WEAK_SERVICE_PROVIDER_H_
diff --git a/mojo/public/cpp/application/service_provider_impl.h b/mojo/public/cpp/application/service_provider_impl.h
new file mode 100644
index 0000000..f388511
--- /dev/null
+++ b/mojo/public/cpp/application/service_provider_impl.h
@@ -0,0 +1,67 @@
+// 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_APPLICATION_SERVICE_PROVIDER_IMPL_H_
+#define MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace mojo {
+namespace internal {
+class WeakServiceProvider;
+class ServiceConnectorBase;
+}
+
+// Implements a registry that can be used to expose services to another app.
+class ServiceProviderImpl : public InterfaceImpl<ServiceProvider> {
+ public:
+ ServiceProviderImpl();
+ virtual ~ServiceProviderImpl();
+
+ template <typename Interface>
+ void AddService(InterfaceFactory<Interface>* factory) {
+ AddServiceConnector(
+ new internal::InterfaceFactoryConnector<Interface>(factory));
+ }
+
+ // Returns an instance of a ServiceProvider that weakly wraps this impl's
+ // connection to some other app. Whereas the lifetime of an instance of
+ // ServiceProviderImpl is bound to the lifetime of the pipe it
+ // encapsulates, the lifetime of the ServiceProvider instance returned by this
+ // method is assumed by the caller. After the pipe is closed
+ // ConnectToService() calls on this object will be silently dropped.
+ // This method must only be called once per ServiceProviderImpl.
+ ServiceProvider* CreateRemoteServiceProvider();
+
+ private:
+ typedef std::map<std::string, internal::ServiceConnectorBase*>
+ NameToServiceConnectorMap;
+
+ friend class internal::WeakServiceProvider;
+
+ // Overridden from ServiceProvider:
+ virtual void ConnectToService(const String& service_name,
+ ScopedMessagePipeHandle client_handle) override;
+
+ // Overridden from InterfaceImpl:
+ virtual void OnConnectionError() override;
+
+ void AddServiceConnector(
+ internal::ServiceConnectorBase* service_connector);
+ void RemoveServiceConnector(
+ internal::ServiceConnectorBase* service_connector);
+
+ void ClearRemote();
+
+ NameToServiceConnectorMap service_connectors_;
+
+ internal::WeakServiceProvider* remote_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceProviderImpl);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
diff --git a/mojo/public/cpp/application/tests/BUILD.gn b/mojo/public/cpp/application/tests/BUILD.gn
new file mode 100644
index 0000000..4c22c19
--- /dev/null
+++ b/mojo/public/cpp/application/tests/BUILD.gn
@@ -0,0 +1,17 @@
+# 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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_application_unittests
+test("mojo_public_application_unittests") {
+ deps = [
+ "//base",
+ "//mojo/common/test:run_all_unittests",
+ "//mojo/public/cpp/application:standalone",
+ "//mojo/public/cpp/environment:standalone",
+ "//mojo/public/cpp/utility",
+ "//testing/gtest",
+ ]
+
+ sources = [ "service_registry_unittest.cc" ]
+}
diff --git a/mojo/public/cpp/application/tests/service_registry_unittest.cc b/mojo/public/cpp/application/tests/service_registry_unittest.cc
new file mode 100644
index 0000000..a0511ff
--- /dev/null
+++ b/mojo/public/cpp/application/tests/service_registry_unittest.cc
@@ -0,0 +1,66 @@
+// 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/application/lib/service_registry.h"
+
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+class TestConnector : public ServiceConnectorBase {
+ public:
+ TestConnector(const std::string& name, int* delete_count)
+ : ServiceConnectorBase(name), delete_count_(delete_count) {}
+ virtual ~TestConnector() { (*delete_count_)++; }
+ virtual void ConnectToService(
+ const std::string& name,
+ ScopedMessagePipeHandle client_handle) override {}
+
+ private:
+ int* delete_count_;
+};
+
+TEST(ServiceRegistryTest, Ownership) {
+ int delete_count = 0;
+
+ // Destruction.
+ {
+ ServiceRegistry registry;
+ registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+ }
+ EXPECT_EQ(1, delete_count);
+
+ // Removal.
+ {
+ ServiceRegistry registry;
+ ServiceConnectorBase* c = new TestConnector("TC1", &delete_count);
+ registry.AddServiceConnector(c);
+ registry.RemoveServiceConnector(c);
+ EXPECT_EQ(2, delete_count);
+ }
+
+ // Multiple.
+ {
+ ServiceRegistry registry;
+ registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+ registry.AddServiceConnector(new TestConnector("TC2", &delete_count));
+ }
+ EXPECT_EQ(4, delete_count);
+
+ // Re-addition.
+ {
+ ServiceRegistry registry;
+ registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+ registry.AddServiceConnector(new TestConnector("TC1", &delete_count));
+ EXPECT_EQ(5, delete_count);
+ }
+ EXPECT_EQ(6, delete_count);
+}
+
+} // namespace
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
new file mode 100644
index 0000000..dad10d8
--- /dev/null
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -0,0 +1,66 @@
+# 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.
+
+source_set("bindings") {
+ sources = [
+ "array.h",
+ "error_handler.h",
+ "interface_ptr.h",
+ "message.h",
+ "message_filter.h",
+ "no_interface.h",
+ "string.h",
+ "struct_ptr.h",
+ "type_converter.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/filter_chain.cc",
+ "lib/filter_chain.h",
+ "lib/fixed_buffer.cc",
+ "lib/fixed_buffer.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/message_queue.cc",
+ "lib/message_queue.h",
+ "lib/no_interface.cc",
+ "lib/router.cc",
+ "lib/router.h",
+ "lib/string_serialization.cc",
+ "lib/string_serialization.h",
+ "lib/validation_errors.cc",
+ "lib/validation_errors.h",
+ ]
+
+ deps = [
+ ":callback",
+ "//mojo/public/cpp/environment",
+ "//mojo/public/cpp/system",
+ ]
+}
+
+source_set("callback") {
+ sources = [
+ "callback.h",
+ "lib/callback_internal.h",
+ "lib/template_util.h",
+ "lib/shared_data.h",
+ "lib/shared_ptr.h",
+ ]
+
+ deps = [ "//mojo/public/cpp/system" ]
+}
diff --git a/mojo/public/cpp/bindings/DEPS b/mojo/public/cpp/bindings/DEPS
new file mode 100644
index 0000000..2a0496e
--- /dev/null
+++ b/mojo/public/cpp/bindings/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/cpp/environment",
+]
diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h
new file mode 100644
index 0000000..53c8c18
--- /dev/null
+++ b/mojo/public/cpp/bindings/array.h
@@ -0,0 +1,159 @@
+// 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 <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// Provides read-only access to array data.
+template <typename T>
+class Array {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(Array, RValue)
+ 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_;
+
+ Array() : is_null_(true) {}
+ explicit Array(size_t size) : vec_(size), is_null_(false) {
+ Traits::Initialize(&vec_);
+ }
+ ~Array() { Traits::Finalize(&vec_); }
+
+ Array(RValue other) : is_null_(true) { Take(other.object); }
+ Array& operator=(RValue other) {
+ Take(other.object);
+ return *this;
+ }
+
+ static Array New(size_t size) {
+ return Array(size).Pass();
+ }
+
+ template <typename U>
+ static Array From(const U& other) {
+ return TypeConverter<Array, U>::Convert(other);
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, Array>::Convert(*this);
+ }
+
+ void reset() {
+ if (!vec_.empty()) {
+ Traits::Finalize(&vec_);
+ vec_.clear();
+ }
+ is_null_ = true;
+ }
+
+ bool is_null() const { return is_null_; }
+
+ ConstRefType front() const { return vec_.front(); }
+ RefType front() { return vec_.front(); }
+
+ size_t size() const { return vec_.size(); }
+
+ 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); }
+
+ void push_back(ForwardType value) {
+ is_null_ = false;
+ Traits::PushBack(&vec_, value);
+ }
+
+ void resize(size_t size) {
+ is_null_ = false;
+ Traits::Resize(&vec_, size);
+ }
+
+ const std::vector<StorageType>& storage() const {
+ return vec_;
+ }
+ operator const std::vector<StorageType>&() const {
+ return vec_;
+ }
+
+ void Swap(Array* other) {
+ std::swap(is_null_, other->is_null_);
+ vec_.swap(other->vec_);
+ }
+ void Swap(std::vector<StorageType>* other) {
+ is_null_ = false;
+ vec_.swap(*other);
+ }
+
+ // 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();
+ }
+
+ private:
+ typedef std::vector<StorageType> Array::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &Array::vec_; }
+
+ private:
+ void Take(Array* other) {
+ reset();
+ Swap(other);
+ }
+
+ std::vector<StorageType> vec_;
+ bool is_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();
+ }
+};
+
+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;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/callback.h b/mojo/public/cpp/bindings/callback.h
new file mode 100644
index 0000000..0cd4924
--- /dev/null
+++ b/mojo/public/cpp/bindings/callback.h
@@ -0,0 +1,522 @@
+// This file was GENERATED by command:
+// pump.py callback.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+
+// 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;
+
+template <>
+class Callback<void()> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run() const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run() const {
+ if (sink_.get())
+ sink_->Run();
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run() const override { sink.Run(); }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1>
+class Callback<void(A1)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(typename internal::Callback_ParamTraits<A1>::ForwardType a1) const {
+ if (sink_.get())
+ sink_->Run(internal::Forward(a1));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(typename internal::Callback_ParamTraits<A1>::ForwardType
+ a1) const override {
+ sink.Run(internal::Forward(a1));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2>
+class Callback<void(A1, A2)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2)
+ const override {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3>
+class Callback<void(A1, A2, A3)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3)
+ const override {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+class Callback<void(A1, A2, A3, A4)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4)
+ const override {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+class Callback<void(A1, A2, A3, A4, A5)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5)
+ const override {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+class Callback<void(A1, A2, A3, A4, A5, A6)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6)
+ const override {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+class Callback<void(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+ typename internal::Callback_ParamTraits<A7>::ForwardType a7) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+ typename internal::Callback_ParamTraits<A7>::ForwardType a7) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6),
+ internal::Forward(a7));
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+ typename internal::Callback_ParamTraits<A7>::ForwardType a7)
+ const override {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6),
+ internal::Forward(a7));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+typedef Callback<void()> Closure;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/callback.h.pump b/mojo/public/cpp/bindings/callback.h.pump
new file mode 100644
index 0000000..856e85b
--- /dev/null
+++ b/mojo/public/cpp/bindings/callback.h.pump
@@ -0,0 +1,95 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$var MAX_ARITY = 7
+
+// 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;
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <$for ARG , [[typename A$(ARG)]]>
+class Callback<void($for ARG , [[A$(ARG)]])> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run($if ARITY > 0 [[
+
+ ]]$for ARG ,
+ [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run($if ARITY > 1 [[
+
+ ]]$for ARG ,
+ [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const {
+ if (sink_.get())
+ sink_->Run($if ARITY > 1 [[
+
+ ]]$for ARG ,
+ [[internal::Forward(a$(ARG))]]);
+ }
+
+ bool is_null() const {
+ return !sink_.get();
+ }
+
+ void reset() {
+ sink_.reset();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run($if ARITY > 0 [[
+
+ ]]$for ARG ,
+ [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const override {
+ sink.Run($if ARITY > 1 [[
+
+ ]]$for ARG ,
+ [[internal::Forward(a$(ARG))]]);
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+]] $$ for ARITY
+
+typedef Callback<void()> Closure;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/error_handler.h b/mojo/public/cpp/bindings/error_handler.h
new file mode 100644
index 0000000..8ce1af2
--- /dev/null
+++ b/mojo/public/cpp/bindings/error_handler.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
+
+namespace mojo {
+
+// This interface is used to report connection errors.
+class ErrorHandler {
+ public:
+ virtual ~ErrorHandler() {}
+ virtual void OnConnectionError() = 0;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
diff --git a/mojo/public/cpp/bindings/interface_impl.h b/mojo/public/cpp/bindings/interface_impl.h
new file mode 100644
index 0000000..da1055d
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_impl.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
+
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/interface_impl_internal.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// InterfaceImpl<..> is designed to be the base class of an interface
+// implementation. It may be bound to a pipe or a proxy, see BindToPipe and
+// BindToProxy.
+template <typename Interface>
+class InterfaceImpl : public internal::InterfaceImplBase<Interface> {
+ public:
+ typedef typename Interface::Client Client;
+ typedef Interface ImplementedInterface;
+
+ InterfaceImpl() : internal_state_(this) {}
+ virtual ~InterfaceImpl() {}
+
+ // Returns a proxy to the client interface. This is null upon construction,
+ // and becomes non-null after OnClientConnected. NOTE: It remains non-null
+ // until this instance is deleted.
+ Client* client() { return internal_state_.client(); }
+
+ // Blocks the current thread for the first incoming method call, i.e., either
+ // a call to a method or a client callback method. Returns |true| if a method
+ // has been called, |false| in case of error. It must only be called on a
+ // bound object.
+ bool WaitForIncomingMethodCall() {
+ return internal_state_.WaitForIncomingMethodCall();
+ }
+
+ // Called when the client has connected to this instance.
+ virtual void OnConnectionEstablished() {}
+
+ // Called when the client is no longer connected to this instance. NOTE: The
+ // client() method continues to return a non-null pointer after this method
+ // is called. After this method is called, any method calls made on client()
+ // will be silently ignored.
+ virtual void OnConnectionError() {}
+
+ // DO NOT USE. Exposed only for internal use and for testing.
+ internal::InterfaceImplState<Interface>* internal_state() {
+ return &internal_state_;
+ }
+
+ private:
+ internal::InterfaceImplState<Interface> internal_state_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceImpl);
+};
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// MessagePipe. The instance is returned for convenience in member initializer
+// lists, etc.
+//
+// If the pipe is closed, the instance's OnConnectionError method will be called
+// and then the instance will be deleted.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then the end
+// point of the pipe will be closed and the error handler's OnConnectionError
+// method will be called.
+//
+// Before returning, the instance's OnConnectionEstablished method is called.
+template <typename Impl>
+Impl* BindToPipe(
+ Impl* instance,
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ instance->internal_state()->Bind(handle.Pass(), true, waiter);
+ return instance;
+}
+
+// Like BindToPipe but does not delete the instance after a channel error.
+template <typename Impl>
+Impl* WeakBindToPipe(
+ Impl* instance,
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ instance->internal_state()->Bind(handle.Pass(), false, waiter);
+ return instance;
+}
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// InterfacePtr<..>. The instance is returned for convenience in member
+// initializer lists, etc. If the pipe is closed, the instance's
+// OnConnectionError method will be called and then the instance will be
+// deleted.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance's OnConnectionEstablished method is called.
+template <typename Impl, typename Interface>
+Impl* BindToProxy(
+ Impl* instance,
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ instance->internal_state()->BindProxy(ptr, true, waiter);
+ return instance;
+}
+
+// Like BindToProxy but does not delete the instance after a channel error.
+template <typename Impl, typename Interface>
+Impl* WeakBindToProxy(
+ Impl* instance,
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ instance->internal_state()->BindProxy(ptr, false, waiter);
+ return instance;
+}
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// InterfaceRequest<..>. The instance is returned for convenience in member
+// initializer lists, etc. If the pipe is closed, the instance's
+// OnConnectionError method will be called and then the instance will be
+// deleted.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance will receive a SetClient call, providing it
+// with a proxy to the client on the other end of the pipe.
+template <typename Impl, typename Interface>
+Impl* BindToRequest(
+ Impl* instance,
+ InterfaceRequest<Interface>* request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ return BindToPipe(instance, request->PassMessagePipe(), waiter);
+}
+
+// Like BindToRequest but does not delete the instance after a channel error.
+template <typename Impl, typename Interface>
+Impl* WeakBindToRequest(
+ Impl* instance,
+ InterfaceRequest<Interface>* request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ return WeakBindToPipe(instance, request->PassMessagePipe(), waiter);
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_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..8dfd465
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -0,0 +1,138 @@
+// 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/error_handler.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 {
+class ErrorHandler;
+
+// InterfacePtr represents a proxy to a remote instance of an interface.
+template <typename Interface>
+class InterfacePtr {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfacePtr, RValue)
+ public:
+ InterfacePtr() {}
+
+ InterfacePtr(RValue other) {
+ internal_state_.Swap(&other.object->internal_state_);
+ }
+ InterfacePtr& operator=(RValue other) {
+ reset();
+ internal_state_.Swap(&other.object->internal_state_);
+ return *this;
+ }
+
+ ~InterfacePtr() {}
+
+ Interface* get() const {
+ return internal_state_.instance();
+ }
+ Interface* operator->() const { return get(); }
+ Interface& operator*() const { return *get(); }
+
+ void reset() {
+ State doomed;
+ internal_state_.Swap(&doomed);
+ }
+
+ // Blocks the current thread for the first incoming method call, i.e., either
+ // a call to a client method or a callback method. Returns |true| if a method
+ // has been called, |false| in case of error. It must only be called on a
+ // bound object.
+ bool WaitForIncomingMethodCall() {
+ return internal_state_.WaitForIncomingMethodCall();
+ }
+
+ // This method configures the InterfacePtr<..> to be a proxy to a remote
+ // object on the other end of the given pipe.
+ //
+ // The proxy is bound to the current thread, which means its methods may
+ // only be called on the current thread.
+ //
+ // To move a bound InterfacePtr<..> to another thread, call PassMessagePipe().
+ // Then create a new InterfacePtr<..> on another thread, and bind the new
+ // InterfacePtr<..> to the message pipe on that thread.
+ void Bind(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ reset();
+ internal_state_.Bind(handle.Pass(), waiter);
+ }
+
+ // The client interface may only be set after this InterfacePtr<..> is bound.
+ void set_client(typename Interface::Client* client) {
+ internal_state_.set_client(client);
+ }
+
+ // This method may be called to query if the underlying pipe has encountered
+ // an error. If true, this means method calls made on this interface will be
+ // dropped (and may have already been dropped) on the floor.
+ bool encountered_error() const {
+ return internal_state_.encountered_error();
+ }
+
+ // This method may be called to register an ErrorHandler to observe a
+ // connection error on the underlying pipe. It must only be called on a bound
+ // object.
+ // The callback runs asynchronously from the current message loop.
+ void set_error_handler(ErrorHandler* error_handler) {
+ internal_state_.set_error_handler(error_handler);
+ }
+
+ // Returns the underlying message pipe handle (if any) and resets the
+ // InterfacePtr<..> to its uninitialized state. This method is helpful if you
+ // need to move a proxy to another thread. See related notes for Bind.
+ ScopedMessagePipeHandle PassMessagePipe() {
+ State state;
+ internal_state_.Swap(&state);
+ return state.PassMessagePipe();
+ }
+
+ // 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:
+ typedef internal::InterfacePtrState<Interface> State;
+ mutable State internal_state_;
+};
+
+// Takes a handle to the proxy end-point of a pipe. On the other end is
+// presumed to be an interface implementation of type |Interface|. Returns a
+// generated proxy to that interface, which may be used on the current thread.
+// It is valid to call set_client on the returned InterfacePtr<..> to set an
+// instance of Interface::Client.
+template <typename Interface>
+InterfacePtr<Interface> MakeProxy(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ InterfacePtr<Interface> ptr;
+ if (handle.is_valid())
+ ptr.Bind(handle.Pass(), waiter);
+ return ptr.Pass();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_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..6b7d303
--- /dev/null
+++ b/mojo/public/cpp/bindings/interface_request.h
@@ -0,0 +1,76 @@
+// 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 {
+
+// Used in methods that return instances of remote objects.
+template <typename Interface>
+class InterfaceRequest {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfaceRequest, RValue)
+ public:
+ InterfaceRequest() {}
+
+ InterfaceRequest(RValue other) {
+ handle_ = other.object->handle_.Pass();
+ }
+ InterfaceRequest& operator=(RValue other) {
+ handle_ = other.object->handle_.Pass();
+ return *this;
+ }
+
+ // Returns true if the request has yet to be completed.
+ bool is_pending() const { return handle_.is_valid(); }
+
+ void Bind(ScopedMessagePipeHandle handle) {
+ handle_ = handle.Pass();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ return handle_.Pass();
+ }
+
+ private:
+ ScopedMessagePipeHandle handle_;
+};
+
+template <typename Interface>
+InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) {
+ InterfaceRequest<Interface> request;
+ request.Bind(handle.Pass());
+ return request.Pass();
+}
+
+// Used to construct a request that synchronously binds an InterfacePtr<..>,
+// making it immediately usable upon return. The resulting request object may
+// then be later bound to an InterfaceImpl<..> via BindToRequest.
+//
+// Given the following interface:
+//
+// interface Foo {
+// CreateBar(Bar& bar);
+// }
+//
+// The caller of CreateBar would have code similar to the following:
+//
+// InterfacePtr<Foo> foo = ...;
+// InterfacePtr<Bar> bar;
+// foo->CreateBar(Get(&bar));
+//
+// Upon return from CreateBar, |bar| is ready to have methods called on it.
+//
+template <typename Interface>
+InterfaceRequest<Interface> Get(InterfacePtr<Interface>* ptr) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ return MakeRequest<Interface>(pipe.handle1.Pass());
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
diff --git a/mojo/public/cpp/bindings/lib/DEPS b/mojo/public/cpp/bindings/lib/DEPS
new file mode 100644
index 0000000..b809b58
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+mojo/public/cpp/bindings",
+ "+mojo/public/cpp/environment",
+ "+mojo/public/cpp/system",
+]
diff --git a/mojo/public/cpp/bindings/lib/TODO b/mojo/public/cpp/bindings/lib/TODO
new file mode 100644
index 0000000..21bcb6f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/TODO
@@ -0,0 +1,6 @@
+TODOs:
+ - Ensure validation checks are solid
+ - Add tests of validation logic
+ - 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..9f62aa2
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.cc
@@ -0,0 +1,75 @@
+// 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..3681d8e
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -0,0 +1,521 @@
+// 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/template_util.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;
+ }
+};
+
+// Array type information needed for valdiation.
+template <uint32_t in_expected_num_elements,
+ bool in_element_is_nullable,
+ typename InElementValidateParams>
+class ArrayValidateParams {
+ public:
+ // Validation information for elements. It is either another specialization of
+ // ArrayValidateParams (if elements are arrays) or NoValidateParams.
+ typedef InElementValidateParams ElementValidateParams;
+
+ // If |expected_num_elements| is not 0, the array is expected to have exactly
+ // that number of elements.
+ static const uint32_t expected_num_elements = in_expected_num_elements;
+ // Whether the elements are nullable.
+ static const bool element_is_nullable = in_element_is_nullable;
+};
+
+// NoValidateParams is used to indicate the end of an ArrayValidateParams chain.
+class NoValidateParams {
+};
+
+// 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) {
+ }
+
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ MOJO_COMPILE_ASSERT(!element_is_nullable,
+ Primitive_type_should_be_non_nullable);
+ MOJO_COMPILE_ASSERT(
+ (IsSame<ElementValidateParams, NoValidateParams>::value),
+ 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);
+
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ MOJO_COMPILE_ASSERT(
+ (IsSame<ElementValidateParams, NoValidateParams>::value),
+ Handle_type_should_not_have_array_validate_params);
+
+ for (uint32_t i = 0; i < header->num_elements; ++i) {
+ if (!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);
+ }
+
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ return ArraySerializationHelper<Handle, true>::
+ ValidateElements<element_is_nullable, ElementValidateParams>(
+ header, elements, bounds_checker);
+ }
+};
+
+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);
+ }
+
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ for (uint32_t i = 0; i < header->num_elements; ++i) {
+ if (!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, ElementValidateParams>::Run(
+ DecodePointerRaw(&elements[i].offset), bounds_checker)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ template <typename T, typename Params>
+ struct ValidateCaller {
+ static bool Run(const void* data, BoundsChecker* bounds_checker) {
+ MOJO_COMPILE_ASSERT(
+ (IsSame<Params, NoValidateParams>::value),
+ Struct_type_should_not_have_array_validate_params);
+
+ return T::Validate(data, bounds_checker);
+ }
+ };
+
+ template <typename T, typename Params>
+ struct ValidateCaller<Array_Data<T>, Params> {
+ static bool Run(const void* data, BoundsChecker* bounds_checker) {
+ return Array_Data<T>::template Validate<Params>(data, bounds_checker);
+ }
+ };
+};
+
+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));
+ }
+
+ template <typename Params>
+ static bool Validate(const void* data, BoundsChecker* bounds_checker) {
+ 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 (Params::expected_num_elements != 0 &&
+ header->num_elements != Params::expected_num_elements) {
+ ReportValidationError(
+ VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ MakeMessageWithExpectedArraySize(
+ "fixed-size array has wrong number of elements",
+ header->num_elements, 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::template ValidateElements<
+ Params::element_is_nullable, typename Params::ElementValidateParams>(
+ &object->header_, object->storage(), bounds_checker);
+ }
+
+ 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() {}
+
+ internal::ArrayHeader header_;
+
+ // Elements of type internal::ArrayDataTraits<T>::StorageType follow.
+};
+MOJO_COMPILE_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..4407de8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -0,0 +1,262 @@
+// 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/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);
+
+// Because ValidateParams requires explicit argument specification, the
+// argument-dependent loopup technique used to omit namespace when calling
+// Serialize_() doesn't seem to work. Therefore, this function is named
+// differently from those Serialize_() overloads.
+template <typename ValidateParams, typename E, typename F>
+inline void SerializeArray_(Array<E> input, internal::Buffer* buf,
+ internal::Array_Data<F>** output);
+
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* data, Array<E>* output);
+
+namespace internal {
+
+template <typename E, typename F, bool move_only = IsMoveOnlyType<E>::value>
+struct ArraySerializer;
+
+template <typename E, typename F> struct ArraySerializer<E, F, false> {
+ MOJO_COMPILE_ASSERT(sizeof(E) == sizeof(F), wrong_array_serializer);
+ static size_t GetSerializedSize(const Array<E>& input) {
+ return sizeof(Array_Data<F>) + Align(input.size() * sizeof(E));
+ }
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static void SerializeElements(
+ Array<E> input, Buffer* buf, Array_Data<F>* output) {
+ MOJO_COMPILE_ASSERT(!element_is_nullable,
+ Primitive_type_should_be_non_nullable);
+ MOJO_COMPILE_ASSERT(
+ (IsSame<ElementValidateParams, NoValidateParams>::value),
+ Primitive_type_should_not_have_array_validate_params);
+
+ 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());
+ memcpy(&result[0], input->storage(), input->size() * sizeof(E));
+ output->Swap(&result);
+ }
+};
+
+template <> struct ArraySerializer<bool, bool, false> {
+ static size_t GetSerializedSize(const Array<bool>& input) {
+ return sizeof(Array_Data<bool>) + Align((input.size() + 7) / 8);
+ }
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static void SerializeElements(
+ Array<bool> input, Buffer* buf, Array_Data<bool>* output) {
+ MOJO_COMPILE_ASSERT(!element_is_nullable,
+ Primitive_type_should_be_non_nullable);
+ MOJO_COMPILE_ASSERT(
+ (IsSame<ElementValidateParams, NoValidateParams>::value),
+ 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);
+ }
+};
+
+template <typename H> struct ArraySerializer<ScopedHandleBase<H>, H, true> {
+ static size_t GetSerializedSize(const Array<ScopedHandleBase<H> >& input) {
+ return sizeof(Array_Data<H>) + Align(input.size() * sizeof(H));
+ }
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static void SerializeElements(Array<ScopedHandleBase<H> > input,
+ Buffer* buf,
+ Array_Data<H>* output) {
+ MOJO_COMPILE_ASSERT(
+ (IsSame<ElementValidateParams, NoValidateParams>::value),
+ 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(
+ !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);
+ }
+};
+
+template <typename S> struct ArraySerializer<S, typename S::Data_*, true> {
+ static size_t GetSerializedSize(const Array<S>& input) {
+ size_t size = sizeof(Array_Data<typename S::Data_*>) +
+ input.size() * sizeof(internal::StructPointer<typename S::Data_>);
+ for (size_t i = 0; i < input.size(); ++i)
+ size += GetSerializedSize_(input[i]);
+ return size;
+ }
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static void SerializeElements(Array<S> input,
+ Buffer* buf,
+ Array_Data<typename S::Data_*>* output) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ typename S::Data_* element;
+ SerializeCaller<S, ElementValidateParams>::Run(
+ input[i].Pass(), buf, &element);
+ output->at(i) = element;
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !element_is_nullable && !element,
+ VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ MakeMessageWithArrayIndex(
+ "null in array expecting valid pointers", input.size(), i));
+ }
+ }
+ static void DeserializeElements(
+ Array_Data<typename S::Data_*>* input, Array<S>* output) {
+ Array<S> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i) {
+ S element;
+ Deserialize_(input->at(i), &element);
+ result[i] = element.Pass();
+ }
+ output->Swap(&result);
+ }
+
+ private:
+ template <typename T, typename Params>
+ struct SerializeCaller {
+ static void Run(T input, Buffer* buf, typename T::Data_** output) {
+ MOJO_COMPILE_ASSERT((IsSame<Params, NoValidateParams>::value),
+ Struct_type_should_not_have_array_validate_params);
+
+ Serialize_(input.Pass(), buf, output);
+ }
+ };
+
+ template <typename T, typename Params>
+ struct SerializeCaller<Array<T>, Params> {
+ static void Run(Array<T> input,
+ Buffer* buf,
+ typename Array<T>::Data_** output) {
+ SerializeArray_<Params>(input.Pass(), buf, output);
+ }
+ };
+};
+
+template <> struct ArraySerializer<String, String_Data*, false> {
+ static size_t GetSerializedSize(const Array<String>& input) {
+ size_t size = sizeof(Array_Data<String_Data*>) +
+ input.size() * sizeof(internal::StringPointer);
+ for (size_t i = 0; i < input.size(); ++i)
+ size += GetSerializedSize_(input[i]);
+ return size;
+ }
+ template <bool element_is_nullable, typename ElementValidateParams>
+ static void SerializeElements(
+ Array<String> input,
+ Buffer* buf,
+ Array_Data<String_Data*>* output) {
+ MOJO_COMPILE_ASSERT(
+ (IsSame<ElementValidateParams,
+ ArrayValidateParams<0, false, NoValidateParams> >::value),
+ 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(
+ !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 ValidateParams, typename E, typename F>
+inline void SerializeArray_(Array<E> input, internal::Buffer* buf,
+ internal::Array_Data<F>** output) {
+ if (input) {
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ ValidateParams::expected_num_elements != 0 &&
+ input.size() != ValidateParams::expected_num_elements,
+ internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ internal::MakeMessageWithExpectedArraySize(
+ "fixed-size array has wrong number of elements",
+ input.size(), ValidateParams::expected_num_elements));
+
+ internal::Array_Data<F>* result =
+ internal::Array_Data<F>::New(input.size(), buf);
+ if (result) {
+ internal::ArraySerializer<E, F>::template SerializeElements<
+ ValidateParams::element_is_nullable,
+ typename ValidateParams::ElementValidateParams>(
+ internal::Forward(input), buf, result);
+ }
+ *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..f12be10
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -0,0 +1,86 @@
+// 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/system/core.h"
+
+namespace mojo {
+class String;
+
+namespace internal {
+template <typename T> class Array_Data;
+
+#pragma pack(push, 1)
+
+struct StructHeader {
+ uint32_t num_bytes;
+ uint32_t num_fields;
+};
+MOJO_COMPILE_ASSERT(sizeof(StructHeader) == 8, bad_sizeof_StructHeader);
+
+struct ArrayHeader {
+ uint32_t num_bytes;
+ uint32_t num_elements;
+};
+MOJO_COMPILE_ASSERT(sizeof(ArrayHeader) == 8, bad_sizeof_ArrayHeader);
+
+template <typename T>
+union StructPointer {
+ uint64_t offset;
+ T* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(StructPointer<char>) == 8, bad_sizeof_StructPointer);
+
+template <typename T>
+union ArrayPointer {
+ uint64_t offset;
+ Array_Data<T>* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(ArrayPointer<char>) == 8, bad_sizeof_ArrayPointer);
+
+union StringPointer {
+ uint64_t offset;
+ Array_Data<char>* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(StringPointer) == 8, bad_sizeof_StringPointer);
+
+#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, 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<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..7161efe
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_serialization.cc
@@ -0,0 +1,117 @@
+// 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/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.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;
+}
+
+bool ValidateEncodedPointer(const uint64_t* offset) {
+ // Cast to uintptr_t so overflow behavior is well defined.
+ return reinterpret_cast<uintptr_t>(offset) + *offset >=
+ reinterpret_cast<uintptr_t>(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 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()));
+}
+
+bool ValidateStructHeader(const void* data,
+ uint32_t min_num_bytes,
+ uint32_t min_num_fields,
+ BoundsChecker* bounds_checker) {
+ MOJO_DCHECK(min_num_bytes >= sizeof(StructHeader));
+
+ 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);
+
+ // TODO(yzshen): Currently our binding code cannot handle structs of smaller
+ // size or with fewer fields than the version that it sees. That needs to be
+ // changed in order to provide backward compatibility.
+ if (header->num_bytes < min_num_bytes ||
+ header->num_fields < min_num_fields) {
+ 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;
+}
+
+} // 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..6bebf90
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_serialization.h
@@ -0,0 +1,83 @@
+// 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/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+class BoundsChecker;
+
+// 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)));
+}
+
+// Checks whether decoding the pointer will overflow and produce a pointer
+// smaller than |offset|.
+bool ValidateEncodedPointer(const uint64_t* 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);
+// Note: This function doesn't validate the encoded handle value.
+void DecodeHandle(Handle* 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);
+}
+
+// If returns true, this function also claims the memory range of the size
+// specified in the struct header, starting from |data|.
+// Note: |min_num_bytes| must be no less than sizeof(StructHeader).
+bool ValidateStructHeader(const void* data,
+ uint32_t min_num_bytes,
+ uint32_t min_num_fields,
+ BoundsChecker* bounds_checker);
+
+} // 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..87f5b7c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bounds_checker.cc
@@ -0,0 +1,76 @@
+// 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..6c47230
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bounds_checker.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_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..f76ebef
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/callback_internal.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_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;
+};
+
+} // 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..c0b70b8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -0,0 +1,205 @@
+// 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/bindings/error_handler.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+Connector::Connector(ScopedMessagePipeHandle message_pipe,
+ const MojoAsyncWaiter* waiter)
+ : error_handler_(nullptr),
+ 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),
+ 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();
+}
+
+bool Connector::WaitForIncomingMessage() {
+ if (error_)
+ return false;
+
+ MojoResult rv = Wait(message_pipe_.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ if (rv != MOJO_RESULT_OK) {
+ NotifyError();
+ return false;
+ }
+ mojo_ignore_result(ReadSingleMessage(&rv));
+ return (rv == MOJO_RESULT_OK);
+}
+
+bool Connector::Accept(Message* message) {
+ MOJO_CHECK(message_pipe_.is_valid());
+
+ if (error_)
+ return false;
+
+ 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) {
+ NotifyError();
+ 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 ||
+ (enforce_errors_from_incoming_receiver_ && !receiver_result)) {
+ NotifyError();
+ 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::NotifyError() {
+ error_ = true;
+ CancelWait();
+ if (error_handler_)
+ error_handler_->OnConnectionError();
+}
+
+} // 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..dc598a6
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/connector.h
@@ -0,0 +1,113 @@
+// 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/lib/message_queue.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());
+ virtual ~Connector();
+
+ // 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_error_handler(ErrorHandler* error_handler) {
+ 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, triggering the error state. Connector is put into a
+ // quiescent state.
+ void CloseMessagePipe();
+
+ // Releases the pipe, not triggering the error state. Connector is put into
+ // a quiescent state.
+ ScopedMessagePipeHandle PassMessagePipe();
+
+ // Waits for the next message on the pipe, blocking until one arrives or an
+ // error happens. Returns |true| if a message has been delivered, |false|
+ // otherwise.
+ bool WaitForIncomingMessage();
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) override;
+
+ 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();
+
+ void NotifyError();
+
+ // Cancels any calls made to |waiter_|.
+ void CancelWait();
+
+ ErrorHandler* 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_;
+
+ // 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/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc
new file mode 100644
index 0000000..6634562
--- /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(RValue other) : sink_(other.object->sink_) {
+ other.object->sink_ = nullptr;
+ filters_.swap(other.object->filters_);
+}
+
+FilterChain& FilterChain::operator=(RValue other) {
+ std::swap(sink_, other.object->sink_);
+ filters_.swap(other.object->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..fc66642
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/filter_chain.h
@@ -0,0 +1,66 @@
+// 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_FOR_CPP_03(FilterChain, RValue)
+
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit FilterChain(MessageReceiver* sink = nullptr);
+
+ // Move-only constructor and operator=.
+ FilterChain(RValue other);
+ FilterChain& operator=(RValue 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..9542ef8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.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/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(size_t size)
+ : ptr_(nullptr),
+ cursor_(0),
+ size_(internal::Align(size)) {
+ // calloc() required to zero memory and thus avoid info leaks.
+ ptr_ = static_cast<char*>(calloc(size_, 1));
+}
+
+FixedBuffer::~FixedBuffer() {
+ free(ptr_);
+}
+
+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;
+}
+
+void* FixedBuffer::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..d23f664
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -0,0 +1,67 @@
+// 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:
+ explicit FixedBuffer(size_t size);
+ virtual ~FixedBuffer();
+
+ // 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.
+ virtual void* Allocate(size_t num_bytes) override;
+
+ size_t size() const { return size_; }
+
+ // 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:
+ char* ptr_;
+ size_t cursor_;
+ size_t size_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBuffer);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_impl_internal.h b/mojo/public/cpp/bindings/lib/interface_impl_internal.h
new file mode 100644
index 0000000..492b182
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_impl_internal.h
@@ -0,0 +1,128 @@
+// 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_IMPL_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class InterfaceImplBase : public Interface {
+ public:
+ virtual ~InterfaceImplBase() {}
+ virtual void OnConnectionEstablished() = 0;
+ virtual void OnConnectionError() = 0;
+};
+
+template <typename Interface>
+class InterfaceImplState : public ErrorHandler {
+ public:
+ typedef typename Interface::Client Client;
+
+ explicit InterfaceImplState(InterfaceImplBase<Interface>* instance)
+ : router_(nullptr),
+ proxy_(nullptr),
+ instance_bound_to_pipe_(false)
+#ifndef NDEBUG
+ ,
+ deleting_instance_due_to_error_(false)
+#endif
+ {
+ MOJO_DCHECK(instance);
+ stub_.set_sink(instance);
+ }
+
+ virtual ~InterfaceImplState() {
+#ifndef NDEBUG
+ MOJO_DCHECK(!instance_bound_to_pipe_ || deleting_instance_due_to_error_);
+#endif
+ delete proxy_;
+ if (router_) {
+ router_->set_error_handler(nullptr);
+ delete router_;
+ }
+ }
+
+ void BindProxy(
+ InterfacePtr<Interface>* ptr,
+ bool instance_bound_to_pipe,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass(), waiter);
+ Bind(pipe.handle1.Pass(), instance_bound_to_pipe, waiter);
+ }
+
+ void Bind(ScopedMessagePipeHandle handle,
+ bool instance_bound_to_pipe,
+ const MojoAsyncWaiter* waiter) {
+ MOJO_CHECK(!router_);
+
+ FilterChain filters;
+ filters.Append<MessageHeaderValidator>();
+ filters.Append<typename Interface::RequestValidator_>();
+ filters.Append<typename Interface::Client::ResponseValidator_>();
+
+ router_ = new Router(handle.Pass(), filters.Pass(), waiter);
+ router_->set_incoming_receiver(&stub_);
+ router_->set_error_handler(this);
+
+ proxy_ = new typename Client::Proxy_(router_);
+
+ instance_bound_to_pipe_ = instance_bound_to_pipe;
+
+ instance()->OnConnectionEstablished();
+ }
+
+ bool WaitForIncomingMethodCall() {
+ MOJO_DCHECK(router_);
+ return router_->WaitForIncomingMessage();
+ }
+
+ Router* router() { return router_; }
+ Client* client() { return proxy_; }
+
+ private:
+ InterfaceImplBase<Interface>* instance() {
+ return static_cast<InterfaceImplBase<Interface>*>(stub_.sink());
+ }
+
+ virtual void OnConnectionError() override {
+ // If the the instance is not bound to the pipe, the instance might choose
+ // to delete itself in the OnConnectionError handler, which would in turn
+ // delete this. Save the error behavior before invoking the error handler
+ // so we can correctly decide what to do.
+ bool bound = instance_bound_to_pipe_;
+ instance()->OnConnectionError();
+ if (!bound)
+ return;
+#ifndef NDEBUG
+ deleting_instance_due_to_error_ = true;
+#endif
+ delete instance();
+ }
+
+ Router* router_;
+ typename Client::Proxy_* proxy_;
+ typename Interface::Stub_ stub_;
+ bool instance_bound_to_pipe_;
+#ifndef NDEBUG
+ bool deleting_instance_due_to_error_;
+#endif
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceImplState);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_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..77386fa
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
@@ -0,0 +1,151 @@
+// 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/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) {}
+
+ ~InterfacePtrState() {
+ // Destruction order matters here. We delete |proxy_| first, even though
+ // |router_| may have a reference to it, so that |~Interface| may have a
+ // shot at generating new outbound messages (ie, invoking client methods).
+ delete proxy_;
+ delete router_;
+ }
+
+ Interface* instance() {
+ ConfigureProxyIfNecessary();
+
+ // This will be null if the object is not bound.
+ return proxy_;
+ }
+
+ void Swap(InterfacePtrState* other) {
+ std::swap(other->proxy_, proxy_);
+ std::swap(other->router_, router_);
+ handle_.swap(other->handle_);
+ std::swap(other->waiter_, waiter_);
+ }
+
+ void Bind(ScopedMessagePipeHandle handle, const MojoAsyncWaiter* waiter) {
+ MOJO_DCHECK(!proxy_);
+ MOJO_DCHECK(!router_);
+ MOJO_DCHECK(!handle_.is_valid());
+ MOJO_DCHECK(!waiter_);
+
+ handle_ = handle.Pass();
+ waiter_ = waiter;
+ }
+
+ bool WaitForIncomingMethodCall() {
+ ConfigureProxyIfNecessary();
+
+ MOJO_DCHECK(router_);
+ return router_->WaitForIncomingMessage();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ if (router_)
+ return router_->PassMessagePipe();
+
+ waiter_ = nullptr;
+ return handle_.Pass();
+ }
+
+ bool is_bound() const {
+ return handle_.is_valid() || router_;
+ }
+
+ void set_client(typename Interface::Client* client) {
+ ConfigureProxyIfNecessary();
+
+ MOJO_DCHECK(proxy_);
+ proxy_->stub.set_sink(client);
+ }
+
+ bool encountered_error() const {
+ return router_ ? router_->encountered_error() : false;
+ }
+
+ void set_error_handler(ErrorHandler* error_handler) {
+ ConfigureProxyIfNecessary();
+
+ MOJO_DCHECK(router_);
+ router_->set_error_handler(error_handler);
+ }
+
+ Router* router_for_testing() {
+ ConfigureProxyIfNecessary();
+ return router_;
+ }
+
+ private:
+ class ProxyWithStub : public Interface::Proxy_ {
+ public:
+ explicit ProxyWithStub(MessageReceiverWithResponder* receiver)
+ : Interface::Proxy_(receiver) {
+ }
+ typename Interface::Client::Stub_ stub;
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ProxyWithStub);
+ };
+
+ 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::Client::RequestValidator_>();
+ filters.Append<typename Interface::ResponseValidator_>();
+
+ router_ = new Router(handle_.Pass(), filters.Pass(), waiter_);
+ waiter_ = nullptr;
+
+ ProxyWithStub* proxy = new ProxyWithStub(router_);
+ router_->set_incoming_receiver(&proxy->stub);
+
+ proxy_ = proxy;
+ }
+
+ ProxyWithStub* proxy_;
+ Router* router_;
+
+ // |proxy_| and |router_| are not initialized until read/write with the
+ // message pipe handle is needed. Before that, |handle_| and |waiter_| store
+ // the arguments of Bind().
+ ScopedMessagePipeHandle handle_;
+ const MojoAsyncWaiter* waiter_;
+
+ 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/message.cc b/mojo/public/cpp/bindings/lib/message.cc
new file mode 100644
index 0000000..cec60c0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -0,0 +1,82 @@
+// 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()
+ : data_num_bytes_(0),
+ data_(nullptr) {
+}
+
+Message::~Message() {
+ free(data_);
+
+ for (std::vector<Handle>::iterator it = handles_.begin();
+ it != handles_.end(); ++it) {
+ if (it->is_valid())
+ CloseRaw(*it);
+ }
+}
+
+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::AdoptData(uint32_t num_bytes, internal::MessageData* data) {
+ MOJO_DCHECK(!data_);
+ data_num_bytes_ = num_bytes;
+ data_ = data;
+}
+
+void Message::Swap(Message* other) {
+ std::swap(data_num_bytes_, other->data_num_bytes_);
+ std::swap(data_, other->data_);
+ std::swap(handles_, other->handles_);
+}
+
+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..c746644
--- /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/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)
+ : buf_(sizeof(MessageHeader) + payload_size) {
+ MessageHeader* header;
+ Allocate(&buf_, &header);
+ header->num_fields = 2;
+ header->name = name;
+}
+
+MessageBuilder::~MessageBuilder() {
+}
+
+void MessageBuilder::Finish(Message* message) {
+ uint32_t num_bytes = static_cast<uint32_t>(buf_.size());
+ message->AdoptData(num_bytes, static_cast<MessageData*>(buf_.Leak()));
+}
+
+MessageBuilder::MessageBuilder(size_t size)
+ : buf_(size) {
+}
+
+MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t flags,
+ uint64_t request_id)
+ : MessageBuilder(sizeof(MessageHeaderWithRequestID) + payload_size) {
+ MessageHeaderWithRequestID* header;
+ Allocate(&buf_, &header);
+ header->num_fields = 3;
+ 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..b4988ff
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -0,0 +1,63 @@
+// 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"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+class MessageBuilder {
+ public:
+ MessageBuilder(uint32_t name, size_t payload_size);
+ ~MessageBuilder();
+
+ Buffer* buffer() { return &buf_; }
+
+ // Call Finish when done making allocations in |buffer()|. Upon return,
+ // |message| will contain the message data, and |buffer()| will no longer be
+ // valid to reference.
+ void Finish(Message* message);
+
+ protected:
+ explicit MessageBuilder(size_t size);
+ 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..a55917a
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -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.
+
+#include "mojo/public/cpp/bindings/lib/message_header_validator.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/validation_errors.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->num_fields == 2) {
+ if (header->num_bytes != sizeof(MessageHeader)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->num_fields == 3) {
+ if (header->num_bytes != sizeof(MessageHeaderWithRequestID)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->num_fields > 3) {
+ 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->num_fields < 3 &&
+ ((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_FLAG_COMBINATION);
+ 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 (!ValidateStructHeader(message->data(), sizeof(MessageHeader), 2,
+ &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..790aa9b
--- /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);
+
+ virtual 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..3c67902
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_internal.h
@@ -0,0 +1,44 @@
+// 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 {
+ uint32_t name;
+ uint32_t flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MessageHeader) == 16, bad_sizeof_MessageHeader);
+
+struct MessageHeaderWithRequestID : MessageHeader {
+ uint64_t request_id;
+};
+MOJO_COMPILE_ASSERT(sizeof(MessageHeaderWithRequestID) == 24,
+ bad_sizeof_MessageHeaderWithRequestID);
+
+struct MessageData {
+ MessageHeader header;
+};
+
+MOJO_COMPILE_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/message_queue.cc b/mojo/public/cpp/bindings/lib/message_queue.cc
new file mode 100644
index 0000000..fd701e9
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_queue.cc
@@ -0,0 +1,48 @@
+// 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_queue.h"
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace internal {
+
+MessageQueue::MessageQueue() {
+}
+
+MessageQueue::~MessageQueue() {
+ while (!queue_.empty())
+ Pop();
+}
+
+bool MessageQueue::IsEmpty() const {
+ return queue_.empty();
+}
+
+Message* MessageQueue::Peek() {
+ MOJO_DCHECK(!queue_.empty());
+ return queue_.front();
+}
+
+void MessageQueue::Push(Message* message) {
+ queue_.push(new Message());
+ queue_.back()->Swap(message);
+}
+
+void MessageQueue::Pop(Message* message) {
+ MOJO_DCHECK(!queue_.empty());
+ queue_.front()->Swap(message);
+ Pop();
+}
+
+void MessageQueue::Pop() {
+ MOJO_DCHECK(!queue_.empty());
+ delete queue_.front();
+ queue_.pop();
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_queue.h b/mojo/public/cpp/bindings/lib/message_queue.h
new file mode 100644
index 0000000..4e46b54
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_queue.h
@@ -0,0 +1,47 @@
+// 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_QUEUE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
+
+#include <queue>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+// A queue for Message objects.
+class MessageQueue {
+ public:
+ MessageQueue();
+ ~MessageQueue();
+
+ bool IsEmpty() const;
+ Message* Peek();
+
+ // This method transfers ownership of |message->data| and |message->handles|
+ // to the message queue, resetting |message| in the process.
+ void Push(Message* message);
+
+ // Removes the next message from the queue, transferring ownership of its
+ // data and handles to the given |message|.
+ void Pop(Message* message);
+
+ // Removes the next message from the queue, discarding its data and handles.
+ // This is meant to be used in conjunction with |Peek|.
+ void Pop();
+
+ private:
+ std::queue<Message*> queue_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessageQueue);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_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..3478840
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -0,0 +1,142 @@
+// 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 MessageReceiver {
+ public:
+ explicit ResponderThunk(const SharedData<Router*>& router)
+ : router_(router) {
+ }
+ virtual ~ResponderThunk() {
+ }
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) override {
+ MOJO_DCHECK(message->has_flag(kMessageIsResponse));
+
+ bool result = false;
+
+ Router* router = router_.value();
+ if (router)
+ result = router->Accept(message);
+
+ return result;
+ }
+
+ private:
+ SharedData<Router*> router_;
+};
+
+// ----------------------------------------------------------------------------
+
+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 (ResponderMap::const_iterator i = responders_.begin();
+ i != responders_.end(); ++i) {
+ delete i->second;
+ }
+}
+
+bool Router::Accept(Message* message) {
+ MOJO_DCHECK(!message->has_flag(kMessageExpectsResponse));
+ return connector_.Accept(message);
+}
+
+bool Router::AcceptWithResponder(Message* message,
+ MessageReceiver* responder) {
+ 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() {
+ testing_mode_ = true;
+ connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool Router::HandleIncomingMessage(Message* message) {
+ if (message->has_flag(kMessageExpectsResponse)) {
+ if (incoming_receiver_) {
+ MessageReceiver* responder = new ResponderThunk(weak_self_);
+ bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+ if (!ok)
+ delete responder;
+ return ok;
+ }
+
+ // If we receive a request expecting a response when the client is not
+ // listening, then we have no choice but to tear down the pipe.
+ connector_.CloseMessagePipe();
+ } 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 incoming_receiver_->Accept(message);
+ // OK to drop message on the floor.
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+
+} // 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..ebd4928
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.h
@@ -0,0 +1,97 @@
+// 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/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/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+class Router : public MessageReceiverWithResponder {
+ public:
+ Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+ virtual ~Router();
+
+ // Sets the receiver to handle messages read from the message pipe that do
+ // not have the kMessageIsResponse flag set.
+ void set_incoming_receiver(MessageReceiverWithResponder* 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_error_handler(ErrorHandler* error_handler) {
+ connector_.set_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 connector_.encountered_error(); }
+
+ void CloseMessagePipe() {
+ connector_.CloseMessagePipe();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ return connector_.PassMessagePipe();
+ }
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) override;
+ virtual bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override;
+
+ // Blocks the current thread for the first incoming method call, i.e., either
+ // a call to a client method or a callback method.
+ bool WaitForIncomingMessage() {
+ return connector_.WaitForIncomingMessage();
+ }
+
+ // 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();
+
+ private:
+ typedef std::map<uint64_t, MessageReceiver*> ResponderMap;
+
+ class HandleIncomingMessageThunk : public MessageReceiver {
+ public:
+ HandleIncomingMessageThunk(Router* router);
+ virtual ~HandleIncomingMessageThunk();
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) override;
+
+ private:
+ Router* router_;
+ };
+
+ bool HandleIncomingMessage(Message* message);
+
+ HandleIncomingMessageThunk thunk_;
+ FilterChain filters_;
+ Connector connector_;
+ SharedData<Router*> weak_self_;
+ MessageReceiverWithResponder* incoming_receiver_;
+ ResponderMap responders_;
+ uint64_t next_request_id_;
+ bool testing_mode_;
+};
+
+} // 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..c7bd54f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/shared_data.h
@@ -0,0 +1,84 @@
+// 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 "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() { ++ref_count_; }
+ void Release() { if (--ref_count_ == 0) delete this; }
+
+ T value;
+
+ private:
+ int ref_count_;
+ 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..899e792
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/shared_ptr.h
@@ -0,0 +1,67 @@
+// 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..a0544c0
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.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/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..bad2a0c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -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.
+
+#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..5991266
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/template_util.h
@@ -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.
+
+#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<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;
+};
+
+template <typename T>
+typename EnableIf<!IsMoveOnlyType<T>::value, T>::type& Forward(T& t) {
+ return t;
+}
+
+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;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_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..47bcf02
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -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.
+
+#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_FLAG_COMBINATION:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION";
+ case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID";
+ }
+
+ 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..6152e60
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -0,0 +1,111 @@
+// 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 oldest version that we
+ // support.
+ // - |num_fields| is smaller than the field number of the oldest version that
+ // we support.
+ // - |num_bytes| and |num_fields| don't match.
+ 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 an invalid flag combination.
+ VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION,
+ // |flags| in the message header indicates that a request ID is required but
+ // there isn't one.
+ VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID,
+};
+
+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/message.h b/mojo/public/cpp/bindings/message.h
new file mode 100644
index 0000000..62801f1
--- /dev/null
+++ b/mojo/public/cpp/bindings/message.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_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();
+
+ // These may only be called on a newly created Message object.
+ void AllocUninitializedData(uint32_t num_bytes);
+ void AdoptData(uint32_t num_bytes, internal::MessageData* data);
+
+ // Swaps data and handles between this Message and another.
+ void Swap(Message* other);
+
+ 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.num_fields >= 3; }
+ 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:
+ uint32_t data_num_bytes_;
+ internal::MessageData* data_; // Heap-allocated using malloc.
+ 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:
+ virtual ~MessageReceiverWithResponder() {}
+
+ // 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;
+};
+
+// 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..f4faafb
--- /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);
+ virtual ~MessageFilter();
+
+ 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);
+
+ virtual bool Accept(Message* message) override;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_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..07587bd
--- /dev/null
+++ b/mojo/public/cpp/bindings/no_interface.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_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 (e.g., when the Mojom "Peer" attribute is not present).
+
+class NoInterfaceProxy;
+class NoInterfaceStub;
+
+class NoInterface {
+ public:
+ static const char* Name_;
+ typedef NoInterfaceProxy Proxy_;
+ typedef NoInterfaceStub Stub_;
+ typedef PassThroughFilter RequestValidator_;
+ typedef PassThroughFilter ResponseValidator_;
+ typedef NoInterface Client;
+ 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; }
+ virtual bool Accept(Message* message) override;
+ virtual 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..cd7db26
--- /dev/null
+++ b/mojo/public/cpp/bindings/string.h
@@ -0,0 +1,150 @@
+// 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 {
+
+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) {
+ }
+ 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 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();
+}
+
+// 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/struct_ptr.h b/mojo/public/cpp/bindings/struct_ptr.h
new file mode 100644
index 0000000..46729dd
--- /dev/null
+++ b/mojo/public/cpp/bindings/struct_ptr.h
@@ -0,0 +1,167 @@
+// 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/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
+
+template <typename Struct>
+class StructPtr {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(StructPtr, RValue);
+ public:
+ typedef typename Struct::Data_ Data_;
+
+ StructPtr() : ptr_(nullptr) {}
+ ~StructPtr() {
+ delete ptr_;
+ }
+
+ StructPtr(RValue other) : ptr_(nullptr) { Take(other.object); }
+ StructPtr& operator=(RValue other) {
+ Take(other.object);
+ 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();
+ }
+
+ private:
+ typedef Struct* StructPtr::*Testable;
+
+ public:
+ operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
+
+ private:
+ friend class internal::StructHelper<Struct>;
+ 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_FOR_CPP_03(InlinedStructPtr, RValue);
+ public:
+ typedef typename Struct::Data_ Data_;
+
+ InlinedStructPtr() : is_null_(true) {}
+ ~InlinedStructPtr() {}
+
+ InlinedStructPtr(RValue other) : is_null_(true) { Take(other.object); }
+ InlinedStructPtr& operator=(RValue other) {
+ Take(other.object);
+ 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();
+ }
+
+ private:
+ typedef Struct InlinedStructPtr::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
+
+ private:
+ friend class internal::StructHelper<Struct>;
+ 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..6a4b2df
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -0,0 +1,46 @@
+# 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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_bindings_unittests
+test("mojo_public_bindings_unittests") {
+ sources = [
+ "array_unittest.cc",
+ "bounds_checker_unittest.cc",
+ "buffer_unittest.cc",
+ "connector_unittest.cc",
+ "handle_passing_unittest.cc",
+ "interface_ptr_unittest.cc",
+ "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",
+ "validation_unittest.cc",
+ ]
+
+ deps = [
+ "//mojo/common/test:run_all_unittests",
+ "//mojo/public/cpp/bindings",
+ "//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",
+ "//testing/gtest",
+ ":mojo_public_bindings_test_utils",
+ ]
+}
+
+source_set("mojo_public_bindings_test_utils") {
+ sources = [
+ "validation_test_input_parser.cc",
+ "validation_test_input_parser.h",
+ ]
+
+ deps = [
+ "//mojo/public/c/system",
+ ]
+}
diff --git a/mojo/public/cpp/bindings/tests/DEPS b/mojo/public/cpp/bindings/tests/DEPS
new file mode 100644
index 0000000..b99d520
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/public/cpp/environment",
+ "+mojo/public/interfaces/bindings/tests",
+]
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..82a705f
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/array_unittest.cc
@@ -0,0 +1,486 @@
+// 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/environment/environment.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::FixedBuffer;
+using mojo::internal::NoValidateParams;
+using mojo::internal::String_Data;
+
+class CopyableType {
+ public:
+ CopyableType() : copied_(false), ptr_(this) { num_instances_++; }
+ CopyableType(const CopyableType& other) : copied_(true), ptr_(other.ptr()) {
+ num_instances_++;
+ }
+ CopyableType& operator=(const CopyableType& other) {
+ copied_ = true;
+ ptr_ = other.ptr();
+ return *this;
+ }
+ ~CopyableType() { num_instances_--; }
+
+ 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_;
+};
+
+size_t CopyableType::num_instances_ = 0;
+
+class MoveOnlyType {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(MoveOnlyType, RValue)
+ public:
+ typedef MoveOnlyType Data_;
+ MoveOnlyType() : moved_(false), ptr_(this) { num_instances_++; }
+ MoveOnlyType(RValue other) : moved_(true), ptr_(other.object->ptr()) {
+ num_instances_++;
+ }
+ MoveOnlyType& operator=(RValue other) {
+ moved_ = true;
+ ptr_ = other.object->ptr();
+ return *this;
+ }
+ ~MoveOnlyType() { num_instances_--; }
+
+ 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_;
+};
+
+size_t MoveOnlyType::num_instances_ = 0;
+
+class ArrayTest : public testing::Test {
+ public:
+ virtual ~ArrayTest() {}
+
+ private:
+ Environment env_;
+};
+
+// 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);
+
+ FixedBuffer buf(size);
+ Array_Data<int32_t>* data;
+ SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>(
+ array.Pass(), &buf, &data);
+
+ 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_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);
+
+ FixedBuffer buf(size);
+ Array_Data<Array_Data<int32_t>*>* data;
+ SerializeArray_<ArrayValidateParams<0, false,
+ ArrayValidateParams<0, false,
+ NoValidateParams>>>(
+ array.Pass(), &buf, &data);
+
+ 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);
+
+ FixedBuffer buf(size);
+ Array_Data<bool>* data;
+ SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>(
+ array.Pass(), &buf, &data);
+
+ 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);
+
+ FixedBuffer buf(size);
+ Array_Data<String_Data*>* data;
+ SerializeArray_<ArrayValidateParams<0, false,
+ ArrayValidateParams<0, false,
+ NoValidateParams>>>(
+ array.Pass(), &buf, &data);
+
+ 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/bounds_checker_unittest.cc b/mojo/public/cpp/bindings/tests/bounds_checker_unittest.cc
new file mode 100644
index 0000000..8378cdd
--- /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);
+}
+
+#ifdef NDEBUG
+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..61424c0
--- /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::FixedBuffer 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 FixedBuffer::Leak passes ownership to the caller.
+TEST(FixedBufferTest, Leak) {
+ void* ptr = nullptr;
+ void* buf_ptr = nullptr;
+ {
+ internal::FixedBuffer 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 FixedBuffer should be empty now.
+ EXPECT_EQ(0u, buf.size());
+ EXPECT_FALSE(buf.Leak());
+ }
+
+ // Since we called Leak, ptr is still writable after FixedBuffer went out of
+ // scope.
+ memset(ptr, 1, 8);
+ free(buf_ptr);
+}
+
+#ifdef NDEBUG
+TEST(FixedBufferTest, TooBig) {
+ internal::FixedBuffer 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/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
new file mode 100644
index 0000000..317f4c8
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -0,0 +1,402 @@
+// 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 "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/message_queue.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+ MessageAccumulator() {
+ }
+
+ virtual bool Accept(Message* message) override {
+ queue_.Push(message);
+ return true;
+ }
+
+ bool IsEmpty() const {
+ return queue_.IsEmpty();
+ }
+
+ void Pop(Message* message) {
+ queue_.Pop(message);
+ }
+
+ private:
+ internal::MessageQueue queue_;
+};
+
+class ConnectorDeletingMessageAccumulator : public MessageAccumulator {
+ public:
+ ConnectorDeletingMessageAccumulator(internal::Connector** connector)
+ : connector_(connector) {}
+
+ virtual bool Accept(Message* message) override {
+ delete *connector_;
+ *connector_ = 0;
+ return MessageAccumulator::Accept(message);
+ }
+
+ private:
+ internal::Connector** connector_;
+};
+
+class ReentrantMessageAccumulator : public MessageAccumulator {
+ public:
+ ReentrantMessageAccumulator(internal::Connector* connector)
+ : connector_(connector), number_of_calls_(0) {}
+
+ virtual bool Accept(Message* message) override {
+ if (!MessageAccumulator::Accept(message))
+ return false;
+ number_of_calls_++;
+ if (number_of_calls_ == 1) {
+ return connector_->WaitForIncomingMessage();
+ }
+ 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() {
+ }
+
+ virtual void SetUp() override {
+ CreateMessagePipe(nullptr, &handle0_, &handle1_);
+ }
+
+ virtual 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.Finish(message);
+ }
+
+ void PumpMessages() {
+ loop_.RunUntilIdle();
+ }
+
+ protected:
+ ScopedMessagePipeHandle handle0_;
+ ScopedMessagePipeHandle handle1_;
+
+ private:
+ Environment env_;
+ RunLoop 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();
+
+ 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();
+
+ 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 RunLoop 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 RunLoop, 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());
+}
+
+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();
+
+ 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());
+}
+
+} // namespace
+} // namespace test
+} // namespace 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..d4cffb4
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
@@ -0,0 +1,350 @@
+// 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/environment/environment.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.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 InterfaceImpl<imported::ImportedInterface> {
+ public:
+ virtual void DoSomething() override { do_something_count_++; }
+
+ static int do_something_count() { return do_something_count_; }
+
+ private:
+ static int do_something_count_;
+};
+int ImportedInterfaceImpl::do_something_count_ = 0;
+
+class SampleNamedObjectImpl : public InterfaceImpl<sample::NamedObject> {
+ public:
+ virtual void SetName(const mojo::String& name) override { name_ = name; }
+
+ virtual void GetName(
+ const mojo::Callback<void(mojo::String)>& callback) override {
+ callback.Run(name_);
+ }
+
+ private:
+ std::string name_;
+};
+
+class SampleFactoryImpl : public InterfaceImpl<sample::Factory> {
+ public:
+ virtual void DoStuff(sample::RequestPtr request,
+ ScopedMessagePipeHandle pipe) 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();
+ client()->DidStuff(response.Pass(), text1);
+
+ if (request->obj)
+ request->obj->DoSomething();
+ }
+
+ virtual void DoStuff2(ScopedDataPipeConsumerHandle pipe) 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));
+
+ client()->DidStuff2(data);
+ }
+
+ virtual void CreateNamedObject(
+ InterfaceRequest<sample::NamedObject> object_request) override {
+ EXPECT_TRUE(object_request.is_pending());
+ BindToRequest(new SampleNamedObjectImpl(), &object_request);
+ }
+
+ // These aren't called or implemented, but exist here to test that the
+ // methods are generated with the correct argument types for imported
+ // interfaces.
+ virtual void RequestImportedInterface(
+ InterfaceRequest<imported::ImportedInterface> imported,
+ const mojo::Callback<void(InterfaceRequest<imported::ImportedInterface>)>&
+ callback) override {}
+ virtual void TakeImportedInterface(
+ imported::ImportedInterfacePtr imported,
+ const mojo::Callback<void(imported::ImportedInterfacePtr)>& callback)
+ override {}
+
+ private:
+ ScopedMessagePipeHandle pipe1_;
+};
+
+class SampleFactoryClientImpl : public sample::FactoryClient {
+ public:
+ SampleFactoryClientImpl() : got_response_(false) {
+ }
+
+ void set_expected_text_reply(const std::string& expected_text_reply) {
+ expected_text_reply_ = expected_text_reply;
+ }
+
+ bool got_response() const {
+ return got_response_;
+ }
+
+ virtual void DidStuff(sample::ResponsePtr response,
+ const String& text_reply) override {
+ EXPECT_EQ(expected_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;
+ }
+
+ virtual void DidStuff2(const String& text_reply) override {
+ got_response_ = true;
+ EXPECT_EQ(expected_text_reply_, text_reply);
+ }
+
+ private:
+ ScopedMessagePipeHandle pipe1_;
+ ScopedMessagePipeHandle pipe3_;
+ std::string expected_text_reply_;
+ bool got_response_;
+};
+
+class HandlePassingTest : public testing::Test {
+ public:
+ virtual void TearDown() {
+ PumpMessages();
+ }
+
+ void PumpMessages() {
+ loop_.RunUntilIdle();
+ }
+
+ private:
+ Environment env_;
+ RunLoop loop_;
+};
+
+TEST_F(HandlePassingTest, Basic) {
+ sample::FactoryPtr factory;
+ BindToProxy(new SampleFactoryImpl(), &factory);
+
+ SampleFactoryClientImpl factory_client;
+ factory_client.set_expected_text_reply(kText1);
+
+ factory.set_client(&factory_client);
+
+ MessagePipe pipe0;
+ EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1));
+
+ MessagePipe pipe1;
+ EXPECT_TRUE(WriteTextMessage(pipe1.handle1.get(), kText2));
+
+ imported::ImportedInterfacePtr imported;
+ BindToProxy(new ImportedInterfaceImpl(), &imported);
+
+ sample::RequestPtr request(sample::Request::New());
+ request->x = 1;
+ request->pipe = pipe1.handle0.Pass();
+ request->obj = imported.Pass();
+ factory->DoStuff(request.Pass(), pipe0.handle0.Pass());
+
+ EXPECT_FALSE(factory_client.got_response());
+ int count_before = ImportedInterfaceImpl::do_something_count();
+
+ PumpMessages();
+
+ EXPECT_TRUE(factory_client.got_response());
+ EXPECT_EQ(1, ImportedInterfaceImpl::do_something_count() - count_before);
+}
+
+TEST_F(HandlePassingTest, PassInvalid) {
+ sample::FactoryPtr factory;
+ BindToProxy(new SampleFactoryImpl(), &factory);
+
+ SampleFactoryClientImpl factory_client;
+ factory.set_client(&factory_client);
+
+ sample::RequestPtr request(sample::Request::New());
+ request->x = 1;
+ factory->DoStuff(request.Pass(), ScopedMessagePipeHandle().Pass());
+
+ EXPECT_FALSE(factory_client.got_response());
+
+ PumpMessages();
+
+ EXPECT_TRUE(factory_client.got_response());
+}
+
+// Verifies DataPipeConsumer can be passed and read from.
+TEST_F(HandlePassingTest, DataPipe) {
+ sample::FactoryPtr factory;
+ BindToProxy(new SampleFactoryImpl(), &factory);
+
+ SampleFactoryClientImpl factory_client;
+ factory.set_client(&factory_client);
+
+ // 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";
+ factory_client.set_expected_text_reply(expected_text_reply);
+ // +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));
+
+ factory->DoStuff2(consumer_handle.Pass());
+
+ EXPECT_FALSE(factory_client.got_response());
+
+ PumpMessages();
+
+ EXPECT_TRUE(factory_client.got_response());
+}
+
+TEST_F(HandlePassingTest, PipesAreClosed) {
+ sample::FactoryPtr factory;
+ BindToProxy(new SampleFactoryImpl(), &factory);
+
+ SampleFactoryClientImpl factory_client;
+ factory.set_client(&factory_client);
+
+ 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());
+ }
+
+ // 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;
+ BindToProxy(new SampleFactoryImpl(), &factory);
+
+ sample::NamedObjectPtr object1;
+ EXPECT_FALSE(object1);
+
+ InterfaceRequest<sample::NamedObject> object1_request = Get(&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(Get(&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..ded6d47
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
@@ -0,0 +1,384 @@
+// 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/error_handler.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class ErrorObserver : public ErrorHandler {
+ public:
+ ErrorObserver() : encountered_error_(false) {
+ }
+
+ bool encountered_error() const { return encountered_error_; }
+
+ virtual void OnConnectionError() override { encountered_error_ = true; }
+
+ private:
+ bool encountered_error_;
+};
+
+class MathCalculatorImpl : public InterfaceImpl<math::Calculator> {
+ public:
+ virtual ~MathCalculatorImpl() {}
+
+ MathCalculatorImpl()
+ : total_(0.0),
+ got_connection_(false) {
+ }
+
+ virtual void OnConnectionEstablished() override { got_connection_ = true; }
+
+ virtual void Clear() override { client()->Output(total_); }
+
+ virtual void Add(double value) override {
+ total_ += value;
+ client()->Output(total_);
+ }
+
+ virtual void Multiply(double value) override {
+ total_ *= value;
+ client()->Output(total_);
+ }
+
+ bool got_connection() const {
+ return got_connection_;
+ }
+
+ private:
+ double total_;
+ bool got_connection_;
+};
+
+class MathCalculatorUIImpl : public math::CalculatorUI {
+ public:
+ explicit MathCalculatorUIImpl(math::CalculatorPtr calculator)
+ : calculator_(calculator.Pass()),
+ output_(0.0) {
+ calculator_.set_client(this);
+ }
+
+ bool WaitForIncomingMethodCall() {
+ return calculator_.WaitForIncomingMethodCall();
+ }
+
+ bool encountered_error() const {
+ return calculator_.encountered_error();
+ }
+
+ void Add(double value) {
+ calculator_->Add(value);
+ }
+
+ void Subtract(double value) {
+ calculator_->Add(-value);
+ }
+
+ void Multiply(double value) {
+ calculator_->Multiply(value);
+ }
+
+ void Divide(double value) {
+ calculator_->Multiply(1.0 / value);
+ }
+
+ double GetOutput() const {
+ return output_;
+ }
+
+ private:
+ // math::CalculatorUI implementation:
+ virtual void Output(double value) override { output_ = value; }
+
+ math::CalculatorPtr calculator_;
+ double output_;
+};
+
+class SelfDestructingMathCalculatorUIImpl : public math::CalculatorUI {
+ public:
+ explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator)
+ : calculator_(calculator.Pass()),
+ nesting_level_(0) {
+ ++num_instances_;
+ calculator_.set_client(this);
+ }
+
+ void BeginTest(bool nested) {
+ nesting_level_ = nested ? 2 : 1;
+ calculator_->Add(1.0);
+ }
+
+ static int num_instances() { return num_instances_; }
+
+ private:
+ virtual ~SelfDestructingMathCalculatorUIImpl() {
+ --num_instances_;
+ }
+
+ virtual void Output(double value) override {
+ if (--nesting_level_ > 0) {
+ // Add some more and wait for re-entrant call to Output!
+ calculator_->Add(1.0);
+ RunLoop::current()->RunUntilIdle();
+ } else {
+ delete this;
+ }
+ }
+
+ math::CalculatorPtr calculator_;
+ int nesting_level_;
+ static int num_instances_;
+};
+
+// static
+int SelfDestructingMathCalculatorUIImpl::num_instances_ = 0;
+
+class ReentrantServiceImpl : public InterfaceImpl<sample::Service> {
+ public:
+ virtual ~ReentrantServiceImpl() {}
+
+ ReentrantServiceImpl()
+ : got_connection_(false), call_depth_(0), max_call_depth_(0) {}
+
+ virtual void OnConnectionEstablished() override { got_connection_ = true; }
+
+ bool got_connection() const {
+ return got_connection_;
+ }
+
+ int max_call_depth() { return max_call_depth_; }
+
+ virtual void Frobinate(sample::FooPtr foo,
+ sample::Service::BazOptions baz,
+ sample::PortPtr port) override {
+ max_call_depth_ = std::max(++call_depth_, max_call_depth_);
+ if (call_depth_ == 1) {
+ EXPECT_TRUE(WaitForIncomingMethodCall());
+ }
+ call_depth_--;
+ }
+
+ virtual void GetPort(mojo::InterfaceRequest<sample::Port> port) override {}
+
+ private:
+ bool got_connection_;
+ int call_depth_;
+ int max_call_depth_;
+};
+
+class InterfacePtrTest : public testing::Test {
+ public:
+ virtual ~InterfacePtrTest() {
+ loop_.RunUntilIdle();
+ }
+
+ void PumpMessages() {
+ loop_.RunUntilIdle();
+ }
+
+ private:
+ Environment env_;
+ RunLoop loop_;
+};
+
+TEST_F(InterfacePtrTest, EndToEnd) {
+ math::CalculatorPtr calc;
+ MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc);
+ EXPECT_TRUE(impl->got_connection());
+
+ // Suppose this is instantiated in a process that has pipe1_.
+ MathCalculatorUIImpl 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* impl = BindToProxy(new MathCalculatorImpl(), &calc);
+ EXPECT_TRUE(impl->got_connection());
+
+ // Suppose this is instantiated in a process that has pipe1_.
+ MathCalculatorUIImpl calculator_ui(calc.Pass());
+
+ EXPECT_EQ(0.0, calculator_ui.GetOutput());
+
+ calculator_ui.Add(2.0);
+ EXPECT_EQ(0.0, calculator_ui.GetOutput());
+ impl->WaitForIncomingMethodCall();
+ calculator_ui.WaitForIncomingMethodCall();
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+
+ calculator_ui.Multiply(5.0);
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+ impl->WaitForIncomingMethodCall();
+ calculator_ui.WaitForIncomingMethodCall();
+ EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+TEST_F(InterfacePtrTest, Movable) {
+ math::CalculatorPtr a;
+ math::CalculatorPtr b;
+ BindToProxy(new MathCalculatorImpl(), &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<math::Calculator>(pipe.handle0.Pass());
+
+ 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, EncounteredError) {
+ math::CalculatorPtr proxy;
+ MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy);
+
+ MathCalculatorUIImpl 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.
+ server->internal_state()->router()->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* server = BindToProxy(new MathCalculatorImpl(), &proxy);
+
+ ErrorObserver error_observer;
+ proxy.set_error_handler(&error_observer);
+
+ MathCalculatorUIImpl 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.
+ server->internal_state()->router()->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
+ // ErrorHandler interface.
+ EXPECT_TRUE(error_observer.encountered_error());
+}
+
+TEST_F(InterfacePtrTest, NoClientAttribute) {
+ // This is a test to ensure the following compiles. The sample::Port interface
+ // does not have an explicit Client attribute.
+ sample::PortPtr port;
+ MessagePipe pipe;
+ port.Bind(pipe.handle0.Pass());
+}
+
+TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) {
+ math::CalculatorPtr proxy;
+ BindToProxy(new MathCalculatorImpl(), &proxy);
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+
+ SelfDestructingMathCalculatorUIImpl* impl =
+ new SelfDestructingMathCalculatorUIImpl(proxy.Pass());
+ impl->BeginTest(false);
+
+ PumpMessages();
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+}
+
+TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) {
+ math::CalculatorPtr proxy;
+ BindToProxy(new MathCalculatorImpl(), &proxy);
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+
+ SelfDestructingMathCalculatorUIImpl* impl =
+ new SelfDestructingMathCalculatorUIImpl(proxy.Pass());
+ impl->BeginTest(true);
+
+ PumpMessages();
+
+ EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
+}
+
+TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
+ sample::ServicePtr proxy;
+ ReentrantServiceImpl* impl = BindToProxy(new ReentrantServiceImpl(), &proxy);
+ EXPECT_TRUE(impl->got_connection());
+
+ proxy->Frobinate(sample::FooPtr(),
+ sample::Service::BAZ_OPTIONS_REGULAR,
+ sample::PortPtr());
+ proxy->Frobinate(sample::FooPtr(),
+ sample::Service::BAZ_OPTIONS_REGULAR,
+ sample::PortPtr());
+
+ PumpMessages();
+
+ EXPECT_EQ(2, impl->max_call_depth());
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
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..3755e64
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
@@ -0,0 +1,151 @@
+// 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 "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/cpp/utility/run_loop.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 InterfaceImpl<sample::Provider> {
+ public:
+ virtual 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);
+ }
+
+ virtual void EchoStrings(
+ const String& a,
+ const String& b,
+ const Callback<void(String, String)>& callback) override {
+ callback.Run(a, b);
+ }
+
+ virtual void EchoMessagePipeHandle(
+ ScopedMessagePipeHandle a,
+ const Callback<void(ScopedMessagePipeHandle)>& callback) override {
+ callback.Run(a.Pass());
+ }
+
+ virtual void EchoEnum(sample::Enum a,
+ const Callback<void(sample::Enum)>& callback) override {
+ callback.Run(a);
+ }
+};
+
+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:
+ virtual ~RequestResponseTest() {
+ loop_.RunUntilIdle();
+ }
+
+ void PumpMessages() {
+ loop_.RunUntilIdle();
+ }
+
+ private:
+ Environment env_;
+ RunLoop loop_;
+};
+
+TEST_F(RequestResponseTest, EchoString) {
+ sample::ProviderPtr provider;
+ BindToProxy(new ProviderImpl(), &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;
+ BindToProxy(new ProviderImpl(), &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;
+ BindToProxy(new ProviderImpl(), &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;
+ BindToProxy(new ProviderImpl(), &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..d74e525
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/router_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.
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/message_queue.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.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.Finish(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.Finish(message);
+}
+
+class MessageAccumulator : public MessageReceiver {
+ public:
+ explicit MessageAccumulator(internal::MessageQueue* queue) : queue_(queue) {
+ }
+
+ virtual bool Accept(Message* message) override {
+ queue_->Push(message);
+ return true;
+ }
+
+ private:
+ internal::MessageQueue* queue_;
+};
+
+class ResponseGenerator : public MessageReceiverWithResponder {
+ public:
+ ResponseGenerator() {
+ }
+
+ virtual bool Accept(Message* message) override { return false; }
+
+ virtual bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override {
+ EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse));
+
+ return SendResponse(message->name(), message->request_id(), responder);
+ }
+
+ bool SendResponse(uint32_t name, uint64_t request_id,
+ MessageReceiver* responder) {
+ Message response;
+ AllocResponseMessage(name, "world", request_id, &response);
+
+ bool result = responder->Accept(&response);
+ delete responder;
+ return result;
+ }
+};
+
+class LazyResponseGenerator : public ResponseGenerator {
+ public:
+ LazyResponseGenerator() : responder_(nullptr), name_(0), request_id_(0) {
+ }
+
+ virtual ~LazyResponseGenerator() {
+ delete responder_;
+ }
+
+ virtual bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override {
+ name_ = message->name();
+ request_id_ = message->request_id();
+ responder_ = responder;
+ return true;
+ }
+
+ bool has_responder() const { return !!responder_; }
+
+ void Complete() {
+ SendResponse(name_, request_id_, responder_);
+ responder_ = nullptr;
+ }
+
+ private:
+ MessageReceiver* responder_;
+ uint32_t name_;
+ uint32_t request_id_;
+};
+
+class RouterTest : public testing::Test {
+ public:
+ RouterTest() {
+ }
+
+ virtual void SetUp() override {
+ CreateMessagePipe(nullptr, &handle0_, &handle1_);
+ }
+
+ virtual void TearDown() override {}
+
+ void PumpMessages() {
+ loop_.RunUntilIdle();
+ }
+
+ protected:
+ ScopedMessagePipeHandle handle0_;
+ ScopedMessagePipeHandle handle1_;
+
+ private:
+ Environment env_;
+ RunLoop 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);
+
+ internal::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("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);
+
+ internal::MessageQueue message_queue;
+ router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
+
+ router1.WaitForIncomingMessage();
+ router0.WaitForIncomingMessage();
+
+ EXPECT_FALSE(message_queue.IsEmpty());
+
+ Message response;
+ message_queue.Pop(&response);
+
+ EXPECT_EQ(std::string("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);
+
+ internal::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());
+}
+
+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);
+
+ internal::MessageQueue message_queue;
+ router0.AcceptWithResponder(&request,
+ new MessageAccumulator(&message_queue));
+
+ PumpMessages();
+
+ EXPECT_TRUE(generator.has_responder());
+
+ }
+
+ generator.Complete(); // 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..2385772
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -0,0 +1,377 @@
+// 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/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#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:
+ virtual void Frobinate(FooPtr foo, BazOptions baz, PortPtr port) 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());
+ }
+ }
+
+ virtual 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:
+ virtual 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);
+ }
+
+ virtual bool AcceptWithResponder(mojo::Message* message,
+ mojo::MessageReceiver* responder) override {
+ return false;
+ }
+};
+
+class BindingsSampleTest : public testing::Test {
+ public:
+ BindingsSampleTest() {}
+ virtual ~BindingsSampleTest() {}
+
+ private:
+ mojo::Environment env_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(BindingsSampleTest);
+};
+
+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());
+
+ 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..8709460
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
@@ -0,0 +1,210 @@
+// 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/environment/environment.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;
+using mojo::internal::NoValidateParams;
+
+// 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:
+ virtual ~SerializationWarningTest() {}
+
+ protected:
+ template <typename T>
+ void TestWarning(T obj, mojo::internal::ValidationError expected_warning) {
+ warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+ mojo::internal::FixedBuffer buf(GetSerializedSize_(obj));
+ typename T::Data_* data;
+ Serialize_(obj.Pass(), &buf, &data);
+
+ EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+ }
+
+ template <typename ValidateParams, typename T>
+ void TestArrayWarning(T obj,
+ mojo::internal::ValidationError expected_warning) {
+ warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
+
+ mojo::internal::FixedBuffer buf(GetSerializedSize_(obj));
+ typename T::Data_* data;
+ SerializeArray_<ValidateParams>(obj.Pass(), &buf, &data);
+
+ EXPECT_EQ(expected_warning, warning_observer_.last_warning());
+ }
+
+ mojo::internal::SerializationWarningObserverForTesting warning_observer_;
+ Environment env_;
+};
+
+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();
+
+ TestArrayWarning<ArrayValidateParams<0, true,
+ ArrayValidateParams<0, true, NoValidateParams> > >(
+ test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+
+ test_array = CreateTestNestedHandleArray();
+ test_array[0] = Array<ScopedHandle>();
+ TestArrayWarning<ArrayValidateParams<0, false,
+ ArrayValidateParams<0, true, NoValidateParams> > >(
+ test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_array = CreateTestNestedHandleArray();
+ test_array[1][0] = ScopedHandle();
+ TestArrayWarning<ArrayValidateParams<0, true,
+ ArrayValidateParams<0, false, NoValidateParams> > >(
+ test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE);
+}
+
+TEST_F(SerializationWarningTest, ArrayOfStrings) {
+ Array<String> test_array(3);
+ for (size_t i = 0; i < test_array.size(); ++i)
+ test_array[i] = "hello";
+
+ TestArrayWarning<ArrayValidateParams<0, true,
+ ArrayValidateParams<0, false, NoValidateParams> > >(
+ test_array.Pass(), mojo::internal::VALIDATION_ERROR_NONE);
+
+ test_array = Array<String>(3);
+ TestArrayWarning<ArrayValidateParams<0, false,
+ ArrayValidateParams<0, false, NoValidateParams> > >(
+ test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER);
+
+ test_array = Array<String>(2);
+ TestArrayWarning<ArrayValidateParams<3, true,
+ ArrayValidateParams<0, false, NoValidateParams> > >(
+ test_array.Pass(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+}
+
+} // 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..6f85443
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/string_unittest.cc
@@ -0,0 +1,65 @@
+// 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 == t);
+ EXPECT_TRUE("hello world" == s);
+ EXPECT_TRUE(s == "hello world");
+ EXPECT_TRUE("not" != s);
+ EXPECT_TRUE(s != "not");
+}
+
+} // 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..7d74498
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -0,0 +1,177 @@
+// 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 "mojo/public/cpp/environment/environment.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);
+}
+
+class StructTest : public testing::Test {
+ public:
+ virtual ~StructTest() {}
+
+ private:
+ Environment env_;
+};
+
+} // namespace
+
+TEST_F(StructTest, Rect) {
+ RectPtr rect;
+ EXPECT_TRUE(rect.is_null());
+ EXPECT_TRUE(!rect);
+ EXPECT_FALSE(rect);
+
+ rect = MakeRect();
+ EXPECT_FALSE(rect.is_null());
+ EXPECT_FALSE(!rect);
+ EXPECT_TRUE(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::FixedBuffer buf(size);
+ internal::Rect_Data* data;
+ Serialize_(rect.Pass(), &buf, &data);
+
+ RectPtr rect2;
+ Deserialize_(data, &rect2);
+
+ CheckRect(*rect2);
+}
+
+// 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::FixedBuffer 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::FixedBuffer buf(size);
+ internal::NamedRegion_Data* data;
+ Serialize_(region.Pass(), &buf, &data);
+
+ NamedRegionPtr region2;
+ Deserialize_(data, ®ion2);
+
+ 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::FixedBuffer buf(size);
+ internal::NamedRegion_Data* data;
+ Serialize_(region.Pass(), &buf, &data);
+
+ NamedRegionPtr region2;
+ Deserialize_(data, ®ion2);
+
+ EXPECT_TRUE(region2->name.is_null());
+ EXPECT_TRUE(region2->rects.is_null());
+}
+
+} // 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..9500839
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
@@ -0,0 +1,208 @@
+// 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/environment/environment.h"
+#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/validation_test_input_parser.cc b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
new file mode 100644
index 0000000..f5b5a01
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
@@ -0,0 +1,406 @@
+// 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) {
+ MOJO_COMPILE_ASSERT(sizeof(float) == 4, float_size_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) {
+ MOJO_COMPILE_ASSERT(sizeof(double) == 8, double_size_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..c8821cd
--- /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 // num_fields
+// [u4]0 // type
+// [u4]1 // flags
+// [u8]1234 // request_id
+// [anchr]message_header
+//
+// // payload
+// [dist4]foo // num_bytes
+// [u4]2 // num_fields
+// [dist8]bar_ptr // x
+// [u4]0xABCD // y
+// [u4]0 // padding
+// [anchr]foo
+//
+// [anchr]bar_ptr
+// [dist4]bar // num_bytes
+// [u4]3 // num_fields
+// [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..bf0894c
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -0,0 +1,434 @@
+// 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 "mojo/public/c/system/macros.h"
+#include "mojo/public/cpp/bindings/interface_impl.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/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/utility/run_loop.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;
+ bool unused MOJO_ALLOW_UNUSED = 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:
+ virtual bool Accept(Message* message) override {
+ return true; // Any message is OK.
+ }
+};
+
+class ValidationTest : public testing::Test {
+ public:
+ virtual ~ValidationTest() {
+ }
+
+ private:
+ Environment env_;
+};
+
+class ValidationIntegrationTest : public ValidationTest {
+ public:
+ ValidationIntegrationTest() : test_message_receiver_(nullptr) {
+ }
+
+ virtual ~ValidationIntegrationTest() {
+ }
+
+ virtual 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());
+ }
+
+ virtual 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()) {
+ }
+ virtual ~TestMessageReceiver() {
+ }
+
+ virtual 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();
+ }
+
+ RunLoop loop_;
+ TestMessageReceiver* test_message_receiver_;
+ ScopedMessagePipeHandle testee_endpoint_;
+};
+
+class IntegrationTestInterface1Client : public IntegrationTestInterface1 {
+ public:
+ virtual ~IntegrationTestInterface1Client() {
+ }
+
+ virtual void Method0(BasicStructPtr param0) override {}
+};
+
+class IntegrationTestInterface1Impl
+ : public InterfaceImpl<IntegrationTestInterface1> {
+ public:
+ virtual ~IntegrationTestInterface1Impl() {
+ }
+
+ virtual void Method0(BasicStructPtr param0) override {}
+};
+
+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());
+}
+
+TEST_F(ValidationTest, NotImplemented) {
+ DummyMessageReceiver dummy_receiver;
+ mojo::internal::FilterChain validators(&dummy_receiver);
+ validators.Append<mojo::internal::MessageHeaderValidator>();
+ validators.Append<ConformanceTestInterface::RequestValidator_>();
+
+ RunValidationTests("not_implemented_", validators.GetHead());
+}
+
+TEST_F(ValidationIntegrationTest, InterfacePtr) {
+ // Test that InterfacePtr<X> applies the correct validators and they don't
+ // conflict with each other:
+ // - MessageHeaderValidator
+ // - X::Client::RequestValidator_
+ // - X::ResponseValidator_
+
+ IntegrationTestInterface1Client interface1_client;
+ IntegrationTestInterface2Ptr interface2_ptr =
+ MakeProxy<IntegrationTestInterface2>(testee_endpoint().Pass());
+ interface2_ptr.set_client(&interface1_client);
+ interface2_ptr.internal_state()->router_for_testing()->EnableTestingMode();
+
+ RunValidationTests("integration_", test_message_receiver());
+}
+
+TEST_F(ValidationIntegrationTest, InterfaceImpl) {
+ // Test that InterfaceImpl<X> applies the correct validators and they don't
+ // conflict with each other:
+ // - MessageHeaderValidator
+ // - X::RequestValidator_
+ // - X::Client::ResponseValidator_
+
+ // |interface1_impl| will delete itself when the pipe is closed.
+ IntegrationTestInterface1Impl* interface1_impl =
+ BindToPipe(new IntegrationTestInterface1Impl(), testee_endpoint().Pass());
+ interface1_impl->internal_state()->router()->EnableTestingMode();
+
+ RunValidationTests("integration_", test_message_receiver());
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
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..a0d6ca7
--- /dev/null
+++ b/mojo/public/cpp/environment/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.
+
+source_set("environment") {
+ sources = [
+ "logging.h",
+ "environment.h",
+ ]
+
+ public_deps = [ "//mojo/public/c/environment" ]
+
+ deps = [ "//mojo/public/cpp/system" ]
+}
+
+source_set("standalone") {
+ sources = [
+ "lib/default_async_waiter.cc",
+ "lib/default_async_waiter.h",
+ "lib/default_logger.cc",
+ "lib/default_logger.h",
+ "lib/environment.cc",
+ "lib/logging.cc",
+ ]
+
+ public_deps = [ ":environment" ]
+
+ deps = [
+ "//mojo/public/c/environment",
+ "//mojo/public/cpp/utility",
+ ]
+}
diff --git a/mojo/public/cpp/environment/environment.h b/mojo/public/cpp/environment/environment.h
new file mode 100644
index 0000000..48f4c26
--- /dev/null
+++ b/mojo/public/cpp/environment/environment.h
@@ -0,0 +1,41 @@
+// 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 {
+
+// 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);
+ ~Environment();
+
+ static const MojoAsyncWaiter* GetDefaultAsyncWaiter();
+ static const MojoLogger* GetDefaultLogger();
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Environment);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
diff --git a/mojo/public/cpp/environment/lib/DEPS b/mojo/public/cpp/environment/lib/DEPS
new file mode 100644
index 0000000..1889e1f
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/public/cpp/environment",
+ "+mojo/public/cpp/utility",
+]
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..b03623e
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_async_waiter.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/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) {
+ }
+
+ virtual ~RunLoopHandlerImpl() {
+ RunLoop::current()->RemoveHandler(handle_);
+ }
+
+ // RunLoopHandler:
+ virtual void OnHandleReady(const Handle& handle) override {
+ NotifyCallback(MOJO_RESULT_OK);
+ }
+
+ virtual 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..af4a628
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/default_logger.cc
@@ -0,0 +1,71 @@
+// 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* message) {
+ if (log_level < g_minimum_log_level)
+ return;
+
+ // TODO(vtl): Add timestamp also?
+ 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/environment.cc b/mojo/public/cpp/environment/lib/environment.cc
new file mode 100644
index 0000000..5fde8f7
--- /dev/null
+++ b/mojo/public/cpp/environment/lib/environment.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/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/utility/run_loop.h"
+
+namespace mojo {
+
+namespace {
+
+const MojoAsyncWaiter* g_default_async_waiter = nullptr;
+const MojoLogger* g_default_logger = nullptr;
+
+void Init(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger) {
+ g_default_async_waiter =
+ default_async_waiter ? default_async_waiter :
+ &internal::kDefaultAsyncWaiter;
+ g_default_logger = default_logger ? default_logger :
+ &internal::kDefaultLogger;
+
+ RunLoop::SetUp();
+}
+
+} // namespace
+
+Environment::Environment() {
+ Init(nullptr, nullptr);
+}
+
+Environment::Environment(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger) {
+ Init(default_async_waiter, default_logger);
+}
+
+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;
+}
+
+} // 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..990626d
--- /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
+
+LogMessage::LogMessage(const char* file, int line, MojoLogLevel log_level)
+ : log_level_(log_level) {
+ // Note: Don't include the log level in the message, since that's passed on.
+ stream_ << GetFilename(file) << '(' << line << "): ";
+}
+
+LogMessage::~LogMessage() {
+ Environment::GetDefaultLogger()->LogMessage(log_level_,
+ stream_.str().c_str());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/environment/logging.h b/mojo/public/cpp/environment/logging.h
new file mode 100644
index 0000000..a3e2cef
--- /dev/null
+++ b/mojo/public/cpp/environment/logging.h
@@ -0,0 +1,87 @@
+// 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(__FILE__, __LINE__, \
+ MOJO_LOG_LEVEL_ ## level).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)
+#define MOJO_DCHECK(condition) MOJO_LAZY_LOG_STREAM(FATAL, false && (condition))
+
+#else
+
+#define MOJO_DLOG(level) MOJO_LOG(level)
+#define MOJO_DLOG_IF(level, condition) MOJO_LOG_IF(level, condition)
+#define MOJO_DCHECK(condition) MOJO_CHECK(condition)
+
+#endif // NDEBUG
+
+namespace mojo {
+namespace internal {
+
+class LogMessage {
+ public:
+ LogMessage(const char* file, int line, MojoLogLevel log_level);
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ private:
+ const MojoLogLevel log_level_;
+ 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/tests/BUILD.gn b/mojo/public/cpp/environment/tests/BUILD.gn
new file mode 100644
index 0000000..55ded10
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/BUILD.gn
@@ -0,0 +1,22 @@
+# 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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_environment_unittests
+test("mojo_public_environment_unittests") {
+ sources = [
+ "async_waiter_unittest.cc",
+ "logger_unittest.cc",
+ "logging_unittest.cc",
+ ]
+
+ deps = [
+ "//mojo/common/test:run_all_unittests",
+ "//mojo/public/c/environment",
+ "//mojo/public/cpp/environment:standalone",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//mojo/public/cpp/utility",
+ "//testing/gtest",
+ ]
+}
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..0992f89
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/async_waiter_unittest.cc
@@ -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.
+
+#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) {
+ }
+ virtual ~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 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()));
+
+ 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(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()));
+
+ 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(AsyncWaiterTest, 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/logger_unittest.cc b/mojo/public/cpp/environment/tests/logger_unittest.cc
new file mode 100644
index 0000000..e30a84b
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/logger_unittest.cc
@@ -0,0 +1,56 @@
+// 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) {
+ Environment environment;
+ const MojoLogger* const logger = Environment::GetDefaultLogger();
+
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE-1, "Logged at VERBOSE-1 level");
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, "Logged at VERBOSE level");
+ logger->LogMessage(MOJO_LOG_LEVEL_INFO, "Logged at INFO level");
+ logger->LogMessage(MOJO_LOG_LEVEL_WARNING, "Logged at WARNING level");
+ logger->LogMessage(MOJO_LOG_LEVEL_ERROR, "Logged at ERROR level");
+
+ // This should kill us:
+ EXPECT_DEATH_IF_SUPPORTED({
+ logger->LogMessage(MOJO_LOG_LEVEL_FATAL, "Logged at FATAL level");
+ }, "");
+}
+
+TEST(LoggerTest, LogLevels) {
+ 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, "Logged at VERBOSE-1 level");
+ logger->LogMessage(MOJO_LOG_LEVEL_VERBOSE, "Logged at VERBOSE level");
+ logger->LogMessage(MOJO_LOG_LEVEL_INFO, "Logged at INFO level");
+ logger->LogMessage(MOJO_LOG_LEVEL_WARNING, "Logged at WARNING level");
+ logger->LogMessage(MOJO_LOG_LEVEL_ERROR, "Logged at ERROR level");
+
+ // This should kill us:
+ EXPECT_DEATH_IF_SUPPORTED({
+ logger->LogMessage(MOJO_LOG_LEVEL_FATAL, "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..45f527e
--- /dev/null
+++ b/mojo/public/cpp/environment/tests/logging_unittest.cc
@@ -0,0 +1,474 @@
+// 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"
+
+// A macro, so it can be automatically joined with other string literals. (Not
+// simply __FILE__, since that may contain a path.)
+#define OUR_FILENAME "logging_unittest.cc"
+
+namespace mojo {
+namespace {
+
+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) {
+ minimum_log_level_ = MOJO_LOG_LEVEL_INFO;
+ ResetMockLogger();
+ }
+ virtual ~LoggingTest() {}
+
+ protected:
+ // Note: Does not reset |minimum_log_level_|.
+ static void ResetMockLogger() {
+ log_message_was_called_ = false;
+ last_log_level_ = MOJO_LOG_LEVEL_INFO;
+ 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_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* message) {
+ log_message_was_called_ = true;
+ last_log_level_ = log_level;
+ 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_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_message_;
+
+// static
+bool LoggingTest::not_called_condition_was_called_ = false;
+
+std::string ExpectedLogMessage(int line, const char* message) {
+ std::ostringstream s;
+ s << OUR_FILENAME "(" << line << "): " << message;
+ return s.str();
+}
+
+TEST_F(LoggingTest, InternalLogMessage) {
+ internal::LogMessage("foo.cc", 123, MOJO_LOG_LEVEL_INFO).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage("./path/to/foo.cc", 123, MOJO_LOG_LEVEL_WARNING).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_WARNING, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage("/path/to/foo.cc", 123, MOJO_LOG_LEVEL_ERROR).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_ERROR, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage("path/to/foo.cc", 123, MOJO_LOG_LEVEL_FATAL).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_FATAL, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(".\\xy\\foo.cc", 123, MOJO_LOG_LEVEL_VERBOSE).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage("xy\\foo.cc", 123, MOJO_LOG_LEVEL_VERBOSE-1).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE-1, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage("C:\\xy\\foo.cc", 123, MOJO_LOG_LEVEL_VERBOSE-9).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_VERBOSE-9, last_log_level());
+ EXPECT_EQ("foo.cc(123): hello world", last_message());
+
+ ResetMockLogger();
+
+ internal::LogMessage(__FILE__, 123, MOJO_LOG_LEVEL_INFO).stream()
+ << "hello " << "world";
+ EXPECT_TRUE(log_message_was_called());
+ EXPECT_EQ(MOJO_LOG_LEVEL_INFO, last_log_level());
+ EXPECT_EQ(OUR_FILENAME "(123): 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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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(ExpectedLogMessage(__LINE__ - 3, "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());
+ // Different compilers have different ideas about the line number of a split
+ // line.
+ int line = __LINE__;
+ EXPECT_EQ(ExpectedLogMessage(line - 5,
+ "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(ExpectedLogMessage(__LINE__ - 6, "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(ExpectedLogMessage(__LINE__ - 6, "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(ExpectedLogMessage(__LINE__ - 6, "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";
+#ifdef NDEBUG
+ 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());
+ // Different compilers have different ideas about the line number of a split
+ // line.
+ int line = __LINE__;
+ EXPECT_EQ(
+ ExpectedLogMessage(line - 10,
+ "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..046ede0
--- /dev/null
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -0,0 +1,17 @@
+# 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.
+
+source_set("system") {
+ sources = [
+ "buffer.h",
+ "core.h",
+ "data_pipe.h",
+ "functions.h",
+ "handle.h",
+ "macros.h",
+ "message_pipe.h"
+ ]
+
+ 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..ad9966a
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.h
@@ -0,0 +1,113 @@
+// 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_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 {
+
+// SharedBufferHandle ----------------------------------------------------------
+
+class SharedBufferHandle : public Handle {
+ public:
+ SharedBufferHandle() {}
+ explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(SharedBufferHandle) == sizeof(Handle),
+ bad_size_for_cpp_SharedBufferHandle);
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedSharedBufferHandle) ==
+ sizeof(SharedBufferHandle),
+ bad_size_for_cpp_ScopedSharedBufferHandle);
+
+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;
+}
+
+// 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 to 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;
+}
+
+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);
+}
+
+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 MOJO_ALLOW_UNUSED =
+ CreateSharedBuffer(nullptr, num_bytes, &handle);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::SharedBuffer(
+ uint64_t num_bytes,
+ const MojoCreateSharedBufferOptions& options) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateSharedBuffer(&options, num_bytes, &handle);
+ 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..b2f6e68
--- /dev/null
+++ b/mojo/public/cpp/system/data_pipe.h
@@ -0,0 +1,138 @@
+// 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_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 {
+
+// DataPipeProducerHandle and DataPipeConsumerHandle ---------------------------
+
+class DataPipeProducerHandle : public Handle {
+ public:
+ DataPipeProducerHandle() {}
+ explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(DataPipeProducerHandle) == sizeof(Handle),
+ bad_size_for_cpp_DataPipeProducerHandle);
+
+typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedDataPipeProducerHandle) ==
+ sizeof(DataPipeProducerHandle),
+ bad_size_for_cpp_ScopedDataPipeProducerHandle);
+
+class DataPipeConsumerHandle : public Handle {
+ public:
+ DataPipeConsumerHandle() {}
+ explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
+ bad_size_for_cpp_DataPipeConsumerHandle);
+
+typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedDataPipeConsumerHandle) ==
+ sizeof(DataPipeConsumerHandle),
+ bad_size_for_cpp_ScopedDataPipeConsumerHandle);
+
+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;
+}
+
+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);
+}
+
+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);
+}
+
+inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ uint32_t num_bytes_written) {
+ return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written);
+}
+
+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);
+}
+
+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);
+}
+
+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 MOJO_ALLOW_UNUSED =
+ CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateDataPipe(&options, &producer_handle, &consumer_handle);
+ 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..d73d27a
--- /dev/null
+++ b/mojo/public/cpp/system/functions.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_
+
+#include "mojo/public/c/system/functions.h"
+
+namespace mojo {
+
+// Standalone functions --------------------------------------------------------
+
+inline MojoTimeTicks GetTimeTicksNow() {
+ return MojoGetTimeTicksNow();
+}
+
+} // 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..72b14c8
--- /dev/null
+++ b/mojo/public/cpp/system/handle.h
@@ -0,0 +1,246 @@
+// 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 handle.
+//
+// More significantly, |WriteMessageRaw()| exposes the full API complexity of
+// |MojoWriteMessage()| (but doesn't require any extra overhead). It takes a raw
+// array of |Handle|s as input, and takes ownership of them (i.e., invalidates
+// them) on *success* (but not on failure). There are a number of reasons for
+// this. First, C++03 |std::vector|s cannot contain the move-only
+// |Scoped...Handle|s. Second, |std::vector|s impose extra overhead
+// (necessitating heap-allocation of the buffer). Third, |std::vector|s wouldn't
+// provide the desired level of flexibility/safety: a vector of handles would
+// have to be all of the same type (probably |Handle|/|ScopedHandle|). Fourth,
+// it's expected to not be used directly, but instead be used by generated
+// bindings.
+//
+// Other |...Raw()| functions expose similar rough edges, e.g., dealing with raw
+// pointers (and lengths) instead of taking |std::vector|s or similar.
+
+// 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_FOR_CPP_03(ScopedHandleBase, RValue)
+
+ 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(RValue other) : handle_(other.object->release()) {}
+ ScopedHandleBase& operator=(RValue other) {
+ if (other.object != this) {
+ CloseIfNecessary();
+ handle_ = other.object->release();
+ }
+ return *this;
+ }
+
+ const HandleType& get() const { return handle_; }
+
+ template <typename PassedHandleType>
+ static ScopedHandleBase<HandleType> From(
+ ScopedHandleBase<PassedHandleType> other) {
+ MOJO_COMPILE_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 MOJO_ALLOW_UNUSED = MojoClose(handle_.value());
+ 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.
+MOJO_COMPILE_ASSERT(sizeof(Handle) == sizeof(MojoHandle),
+ bad_size_for_cpp_Handle);
+
+// The scoper should also impose no more overhead.
+typedef ScopedHandleBase<Handle> ScopedHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedHandle) == sizeof(Handle),
+ bad_size_for_cpp_ScopedHandle);
+
+inline MojoResult Wait(Handle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ return MojoWait(handle.value(), signals, deadline);
+}
+
+// |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>
+inline MojoResult WaitMany(const HandleVectorType& handles,
+ const FlagsVectorType& signals,
+ MojoDeadline deadline) {
+ if (signals.size() != handles.size())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (handles.size() > std::numeric_limits<uint32_t>::max())
+ return MOJO_RESULT_OUT_OF_RANGE;
+
+ if (handles.size() == 0)
+ return MojoWaitMany(nullptr, nullptr, 0, deadline);
+
+ const Handle& first_handle = handles[0];
+ const MojoHandleSignals& first_signals = signals[0];
+ return MojoWaitMany(
+ reinterpret_cast<const MojoHandle*>(&first_handle),
+ reinterpret_cast<const MojoHandleSignals*>(&first_signals),
+ static_cast<uint32_t>(handles.size()),
+ deadline);
+}
+
+// |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..605906e
--- /dev/null
+++ b/mojo/public/cpp/system/macros.h
@@ -0,0 +1,48 @@
+// 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_MACROS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
+
+#include "mojo/public/c/system/macros.h"
+
+// 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.
+
+// A macro to disallow the copy constructor and operator= functions.
+// This should be used in the private: declarations for a class.
+#define MOJO_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// Used to calculate the number of elements in an array.
+// (See |arraysize()| in Chromium's base/basictypes.h for more details.)
+namespace mojo {
+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 mojo
+#define MOJO_ARRAYSIZE(array) (sizeof(::mojo::ArraySizeHelper(array)))
+
+// Used to make a type move-only in C++03. See Chromium's base/move.h for more
+// details.
+#define MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
+ private: \
+ struct rvalue_type { \
+ explicit rvalue_type(type* object) : object(object) {} \
+ type* object; \
+ }; \
+ type(type&); \
+ void operator=(type&); \
+ public: \
+ operator rvalue_type() { return rvalue_type(this); } \
+ type Pass() { return type(rvalue_type(this)); } \
+ typedef void MoveOnlyTypeForCPP03; \
+ private:
+
+#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..7ef6314
--- /dev/null
+++ b/mojo/public/cpp/system/message_pipe.h
@@ -0,0 +1,103 @@
+// 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_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 {
+
+// MessagePipeHandle -----------------------------------------------------------
+
+class MessagePipeHandle : public Handle {
+ public:
+ MessagePipeHandle() {}
+ explicit MessagePipeHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(MessagePipeHandle) == sizeof(Handle),
+ bad_size_for_cpp_MessagePipeHandle);
+
+typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedMessagePipeHandle) ==
+ sizeof(MessagePipeHandle),
+ bad_size_for_cpp_ScopedMessagePipeHandle);
+
+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;
+}
+
+// These "raw" versions fully expose the underlying API, but don't help with
+// ownership of handles (especially when writing messages).
+// TODO(vtl): Write "baked" versions.
+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);
+}
+
+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 MOJO_ALLOW_UNUSED =
+ CreateMessagePipe(nullptr, &handle0, &handle1);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateMessagePipe(&options, &handle0, &handle1);
+ 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..3226662
--- /dev/null
+++ b/mojo/public/cpp/system/tests/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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_system_unittests
+test("mojo_public_system_unittests") {
+ deps = [
+ "//mojo/common/test:run_all_unittests",
+ "//mojo/public/c/system/tests",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//testing/gtest",
+ ]
+
+ sources = [
+ "core_unittest.cc",
+ "macros_unittest.cc",
+ ]
+}
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..a44bc07
--- /dev/null
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -0,0 +1,419 @@
+// 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 <map>
+
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+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));
+
+ std::vector<Handle> wh;
+ wh.push_back(h.get());
+ std::vector<MojoHandleSignals> sigs;
+ sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE);
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE));
+ }
+
+ // |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();
+
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 0));
+ 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);
+ EXPECT_EQ(1, WaitMany(wh, sigs, 1000));
+
+ // Test closing |h1| explicitly.
+ Close(h1.Pass());
+ EXPECT_FALSE(h1.get().is_valid());
+
+ // Make sure |h1| is closed.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWait(hv1, ~MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_DEADLINE_INDEFINITE));
+
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ }
+ // |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));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ 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));
+ 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));
+ 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..49a8005
--- /dev/null
+++ b/mojo/public/cpp/system/tests/macros_unittest.cc
@@ -0,0 +1,125 @@
+// 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 {
+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 {
+ virtual ~TestOverrideSubclass() {}
+ virtual void ToBeOverridden() override {}
+ virtual 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 |MOJO_COMPILE_ASSERT()|.
+const int kGlobalArray[5] = { 1, 2, 3, 4, 5 };
+MOJO_COMPILE_ASSERT(MOJO_ARRAYSIZE(kGlobalArray) == 5u,
+ mojo_array_size_failed_in_compile_assert);
+
+TEST(MacrosCppTest, ArraySize) {
+ double local_array[4] = { 6.7, 7.8, 8.9, 9.0 };
+ 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_FOR_CPP_03(MoveOnlyInt, RValue)
+
+ public:
+ MoveOnlyInt() : is_set_(false), value_() {}
+ explicit MoveOnlyInt(int value) : is_set_(true), value_(value) {}
+ ~MoveOnlyInt() {}
+
+ // Move-only constructor and operator=.
+ MoveOnlyInt(RValue other) { *this = other; }
+ MoveOnlyInt& operator=(RValue other) {
+ if (other.object != this) {
+ is_set_ = other.object->is_set_;
+ value_ = other.object->value_;
+ other.object->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, MoveOnlyTypeForCpp03) {
+ 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());
+}
+
+} // 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..6c73c29
--- /dev/null
+++ b/mojo/public/cpp/test_support/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_test_utils
+source_set("test_utils") {
+ testonly = true
+ deps = [
+ "//base",
+ "//mojo/public/c/test_support",
+ "//mojo/public/cpp/system",
+ "//testing/gtest",
+ ]
+
+ sources = [
+ "lib/test_support.cc",
+ "lib/test_utils.cc",
+ "test_utils.h",
+ ]
+}
diff --git a/mojo/public/cpp/test_support/DEPS b/mojo/public/cpp/test_support/DEPS
new file mode 100644
index 0000000..6dc5394
--- /dev/null
+++ b/mojo/public/cpp/test_support/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/c/test_support",
+]
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..8491c4b
--- /dev/null
+++ b/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -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.
+
+#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);
+ 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,
+ 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,
+ 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..eb4d4be
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_support.h
@@ -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.
+
+#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,
+ double value,
+ const char* units) {
+ MojoTestSupportLogPerfResult(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..43a3ea9
--- /dev/null
+++ b/mojo/public/cpp/test_support/test_utils.h
@@ -0,0 +1,39 @@
+// 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,
+ 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..a0898b9
--- /dev/null
+++ b/mojo/public/cpp/utility/BUILD.gn
@@ -0,0 +1,33 @@
+# 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.
+
+source_set("utility") {
+ sources = [
+ "mutex.h",
+ "run_loop.h",
+ "run_loop_handler.h",
+ "thread.h",
+ "lib/mutex.cc",
+ "lib/run_loop.cc",
+ "lib/thread.cc",
+ "lib/thread_local.h",
+ "lib/thread_local_posix.cc",
+ "lib/thread_local_win.cc",
+ ]
+
+ deps = [
+ "//mojo/public/cpp/bindings:callback",
+ "//mojo/public/cpp/system",
+ ]
+
+ if (is_win) {
+ # See crbug.com/342893:
+ sources -= [
+ "mutex.h",
+ "thread.h",
+ "lib/mutex.cc",
+ "lib/thread.cc",
+ ]
+ }
+}
diff --git a/mojo/public/cpp/utility/DEPS b/mojo/public/cpp/utility/DEPS
new file mode 100644
index 0000000..a9dfbd1
--- /dev/null
+++ b/mojo/public/cpp/utility/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/cpp/bindings/callback.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..bcbc623
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/run_loop.cc
@@ -0,0 +1,278 @@
+// 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_(NULL), 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(NULL);
+}
+
+// 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() && delayed_tasks_.empty()) {
+ Quit();
+ return false;
+ }
+
+ const MojoResult result = WaitMany(wait_state.handles,
+ wait_state.handle_signals,
+ wait_state.deadline);
+ if (result >= 0) {
+ const size_t index = static_cast<size_t>(result);
+ assert(handler_data_.find(wait_state.handles[index]) !=
+ handler_data_.end());
+ handler_data_[wait_state.handles[index]].handler->OnHandleReady(
+ wait_state.handles[index]);
+ return true;
+ }
+
+ switch (result) {
+ case MOJO_RESULT_INVALID_ARGUMENT:
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ return RemoveFirstInvalidHandle(wait_state);
+ case MOJO_RESULT_DEADLINE_EXCEEDED:
+ return NotifyHandlers(MOJO_RESULT_DEADLINE_EXCEEDED, CHECK_DEADLINE);
+ }
+
+ 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;
+}
+
+bool RunLoop::RemoveFirstInvalidHandle(const WaitState& wait_state) {
+ for (size_t i = 0; i < wait_state.handles.size(); ++i) {
+ const MojoResult result =
+ mojo::Wait(wait_state.handles[i], wait_state.handle_signals[i],
+ static_cast<MojoDeadline>(0));
+ if (result == MOJO_RESULT_INVALID_ARGUMENT ||
+ result == MOJO_RESULT_FAILED_PRECONDITION) {
+ // Remove the handle first, this way if OnHandleError() tries to remove
+ // the handle our iterator isn't invalidated.
+ assert(handler_data_.find(wait_state.handles[i]) != handler_data_.end());
+ RunLoopHandler* handler =
+ handler_data_[wait_state.handles[i]].handler;
+ handler_data_.erase(wait_state.handles[i]);
+ handler->OnHandleError(wait_state.handles[i], result);
+ return true;
+ }
+ assert(MOJO_RESULT_DEADLINE_EXCEEDED == result);
+ }
+ return false;
+}
+
+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..da33497
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread.cc
@@ -0,0 +1,69 @@
+// 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 MOJO_ALLOW_UNUSED = pthread_attr_init(&attr);
+ 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 MOJO_ALLOW_UNUSED = pthread_join(thread_, NULL);
+ assert(rv == 0);
+}
+
+// static
+void* Thread::ThreadRunTrampoline(void* arg) {
+ Thread* self = static_cast<Thread*>(arg);
+ self->Run();
+ return NULL;
+}
+
+} // 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..4c3625d
--- /dev/null
+++ b/mojo/public/cpp/utility/lib/thread_local.h
@@ -0,0 +1,61 @@
+// 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..b33dfc6
--- /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, NULL) != 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..98841f7
--- /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 <assert.h>
+#include <windows.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..35611c2
--- /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) MOJO_COMPILE_ASSERT(0, mutex_lock_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..359208d
--- /dev/null
+++ b/mojo/public/cpp/utility/run_loop.h
@@ -0,0 +1,155 @@
+// 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;
+
+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. Only one handler can
+ // be registered for a specified handle.
+ 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(NULL),
+ 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);
+
+ // Removes the first invalid handle. This is called if MojoWaitMany() finds an
+ // invalid handle. Returns true if a RunLoopHandler was notified.
+ bool RemoveFirstInvalidHandle(const WaitState& wait_state);
+
+ // 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..225bf07
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/BUILD.gn
@@ -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.
+
+# GYP version: mojo/mojo_public_tests.gypi:mojo_public_utility_unittests
+test("mojo_public_utility_unittests") {
+ deps = [
+ "//mojo/common/test:run_all_unittests",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//mojo/public/cpp/utility",
+ "//testing/gtest",
+ ]
+
+ sources = [
+ "mutex_unittest.cc",
+ "run_loop_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..eb5a330
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/mutex_unittest.cc
@@ -0,0 +1,260 @@
+// 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 MOJO_ALLOW_UNUSED = nanosleep(&req, NULL);
+ 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) {
+ }
+
+ virtual ~FiddlerThread() {
+ delete fiddler_;
+ }
+
+ virtual 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_() {}
+ virtual ~TryThread() {}
+
+ virtual 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..c6199d7
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/run_loop_unittest.cc
@@ -0,0 +1,431 @@
+// 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) {
+ }
+ virtual ~TestRunLoopHandler() {}
+
+ 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:
+ virtual void OnHandleReady(const Handle& handle) override { ready_count_++; }
+ virtual 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() {}
+
+ virtual void SetUp() override {
+ Test::SetUp();
+ RunLoop::SetUp();
+ }
+ virtual 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_(NULL) {
+ }
+ virtual ~RemoveOnReadyRunLoopHandler() {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ virtual 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_(NULL) {
+ }
+ virtual ~QuitOnReadyRunLoopHandler() {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ virtual 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_(NULL) {
+ }
+ virtual ~QuitOnErrorRunLoopHandler() {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ virtual 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_(NULL) {
+ }
+ virtual ~RemoveManyRunLoopHandler() {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+ void add_handle(const Handle& handle) { handles_.push_back(handle); }
+
+ // RunLoopHandler:
+ virtual 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_(NULL) {
+ }
+ virtual ~AddHandlerOnErrorHandler() {}
+
+ void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ // RunLoopHandler:
+ virtual 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() == NULL);
+ {
+ RunLoop run_loop;
+ EXPECT_EQ(&run_loop, RunLoop::current());
+ }
+ EXPECT_TRUE(RunLoop::current() == NULL);
+}
+
+class NestingRunLoopHandler : public TestRunLoopHandler {
+ public:
+ static const size_t kDepthLimit;
+ static const char kSignalMagic;
+
+ NestingRunLoopHandler()
+ : run_loop_(NULL),
+ pipe_(NULL),
+ depth_(0),
+ reached_depth_limit_(false) {}
+
+ virtual ~NestingRunLoopHandler() {}
+
+ 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:
+ virtual 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, NULL, 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, NULL, &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..0d81ef8
--- /dev/null
+++ b/mojo/public/cpp/utility/tests/thread_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/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) {
+ }
+
+ virtual ~SetIntThread() {
+ }
+
+ virtual 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_
diff --git a/mojo/public/gles2/BUILD.gn b/mojo/public/gles2/BUILD.gn
new file mode 100644
index 0000000..5c21e78
--- /dev/null
+++ b/mojo/public/gles2/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.
+
+# In an is_component_build build, everything can link against //mojo/gles2
+# because it is built as a shared library. However, in a static build,
+# //mojo/gles2 is linked into an executable (e.g., mojo_shell), and must be
+# injected into other shared libraries (i.e., Mojo Apps) that need the mojo
+# gles2 API.
+#
+# For component targets, add //mojo/public/gles2:for_component to your deps
+# section.
+#
+# For shared_library targets (e.g., a Mojo App), add
+# //mojo/public/gles2:for_shared_library to your deps
+
+group("for_shared_library") {
+ public_configs = [ "//third_party/khronos:khronos_headers" ]
+ public_deps = [ "//mojo/public/c/gles2" ]
+
+ if (is_component_build) {
+ deps = [ "//mojo/gles2" ]
+ } else {
+ deps = [ "//mojo/public/platform/native:gles2_thunks" ]
+ }
+}
+
+group("for_component") {
+ public_configs = [ "//third_party/khronos:khronos_headers" ]
+ public_deps = [ "//mojo/public/c/gles2" ]
+
+ if (is_component_build) {
+ deps = [ "//mojo/gles2" ]
+ }
+}
diff --git a/mojo/public/go/mojo/system/core.go b/mojo/public/go/mojo/system/core.go
new file mode 100644
index 0000000..8049d37
--- /dev/null
+++ b/mojo/public/go/mojo/system/core.go
@@ -0,0 +1,9 @@
+// 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.
+
+package system;
+
+type Core interface {
+ GetTimeTicksNow() int64
+}
diff --git a/mojo/public/interfaces/application/BUILD.gn b/mojo/public/interfaces/application/BUILD.gn
new file mode 100644
index 0000000..91e2447
--- /dev/null
+++ b/mojo/public/interfaces/application/BUILD.gn
@@ -0,0 +1,14 @@
+# 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/public/tools/bindings/mojom.gni")
+
+# GYP version: mojo/mojo_public.gypi:mojo_application_bindings
+mojom("application") {
+ sources = [
+ "application.mojom",
+ "service_provider.mojom",
+ "shell.mojom",
+ ]
+}
diff --git a/mojo/public/interfaces/application/application.mojom b/mojo/public/interfaces/application/application.mojom
new file mode 100644
index 0000000..758a053
--- /dev/null
+++ b/mojo/public/interfaces/application/application.mojom
@@ -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/public/interfaces/application/service_provider.mojom"
+
+module mojo {
+
+// Applications vend Services through the ServiceProvider interface. Services
+// implement Interfaces.
+[Client=Shell]
+interface Application {
+ // Initialize is guaranteed to be called before any AcceptConnection calls.
+ Initialize(array<string>? args);
+
+ AcceptConnection(string? requestor_url, ServiceProvider? provider);
+};
+
+}
diff --git a/mojo/public/interfaces/application/service_provider.mojom b/mojo/public/interfaces/application/service_provider.mojom
new file mode 100644
index 0000000..00eb057
--- /dev/null
+++ b/mojo/public/interfaces/application/service_provider.mojom
@@ -0,0 +1,16 @@
+// 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.
+
+module mojo {
+
+// ServiceProvider is used to establish connections to services in a bi-
+// directional fashion between two applications.
+[Client=ServiceProvider]
+interface ServiceProvider {
+ // Connect the given message pipe handle to the named service. If the named
+ // service does not exist, then the handle will be closed.
+ ConnectToService(string? interface_name, handle<message_pipe>? pipe);
+};
+
+}
diff --git a/mojo/public/interfaces/application/shell.mojom b/mojo/public/interfaces/application/shell.mojom
new file mode 100644
index 0000000..a9ce05f
--- /dev/null
+++ b/mojo/public/interfaces/application/shell.mojom
@@ -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.
+
+import "mojo/public/interfaces/application/service_provider.mojom"
+
+module mojo {
+
+// The Shell is the finder and launcher of Applications. An Application uses
+// it's Shell interface to connect to other Applications.
+[Client=Application]
+interface Shell {
+ // Loads url. mojo:{service} will result in the user of the value of the
+ // --origin flag to the shell being used.
+ ConnectToApplication(string? application_url, ServiceProvider&? provider);
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
new file mode 100644
index 0000000..e56f9b5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("test_interfaces") {
+ testonly = true
+ sources = [
+ "math_calculator.mojom",
+ "no_module.mojom",
+ "rect.mojom",
+ "regression_tests.mojom",
+ "sample_factory.mojom",
+ "sample_import.mojom",
+ "sample_import2.mojom",
+ "sample_interfaces.mojom",
+ "sample_service.mojom",
+ "serialization_test_structs.mojom",
+ "test_structs.mojom",
+ "validation_test_interfaces.mojom",
+ ]
+}
diff --git a/mojo/public/interfaces/bindings/tests/data/message_data b/mojo/public/interfaces/bindings/tests/data/message_data
new file mode 100644
index 0000000..b288878
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/message_data
@@ -0,0 +1,25 @@
+// File generated by mojo_message_generator.
+0X10
+0X00
+0X00
+0X00
+0X02
+0X00
+0X00
+0X00
+0X15
+0X00
+0X00
+0X00
+0X00
+0X00
+0X00
+0X00
+0X09
+0X08
+0X07
+0X06
+0X00
+0X00
+0X00
+0X00
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.data
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_empty.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data
new file mode 100644
index 0000000..ee5ecdb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.data
@@ -0,0 +1,2 @@
+[u4]16 // num_bytes: Bigger than the total size of the message.
+[u4]2 // num_fields
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data
new file mode 100644
index 0000000..21e7fbc
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.data
@@ -0,0 +1 @@
+0x00
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_incomplete_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data
new file mode 100644
index 0000000..9d66188
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data
@@ -0,0 +1,6 @@
+[dist4]message_header // num_bytes
+[u4]3 // num_fields
+[u4]0x80000000 // name
+[u4]3 // flags: This combination is illegal.
+[u8]1 // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected
new file mode 100644
index 0000000..696c78d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data
new file mode 100644
index 0000000..2414431
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.data
@@ -0,0 +1,6 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0x80000000 // name
+[u4]1 // flags: This is a response message which expects to
+ // have a request ID.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected
new file mode 100644
index 0000000..083db1a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_missing_request_id.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data
new file mode 100644
index 0000000..ad3b005
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.data
@@ -0,0 +1,4 @@
+[u4]0xFFFFFFFF // num_bytes: Test whether a huge value will cause overflow.
+[u4]2 // num_fields
+[u4]0x80000000 // name
+[u4]0 // flags
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_huge.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data
new file mode 100644
index 0000000..353b4e8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.data
@@ -0,0 +1,4 @@
+[dist4]message_header // num_bytes: Less than the minimal size of message
+ // header.
+[u4]2 // num_fields
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data
new file mode 100644
index 0000000..3a94448
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.data
@@ -0,0 +1,4 @@
+[u4]0 // num_bytes
+[u4]0 // num_fields
+[u4]0x80000000 // name
+[u4]0 // flags
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_less_than_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.data
new file mode 100644
index 0000000..7e8a714
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.data
@@ -0,0 +1,7 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0x80000000 // name
+[u4]0 // flags
+[u8]0 // Extra bytes that result in mismatched |num_bytes| and
+ // |num_fields|.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_1.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.data
new file mode 100644
index 0000000..fd8b4b4
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.data
@@ -0,0 +1,8 @@
+[dist4]message_header // num_bytes
+[u4]3 // num_fields
+[u4]0x80000000 // name
+[u4]0 // flags
+[u8]0 // request_id
+[u8]0 // Extra bytes that result in mismatched |num_bytes| and
+ // |num_fields|.
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.data
new file mode 100644
index 0000000..c4b46ab
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.data
@@ -0,0 +1,5 @@
+[dist4]message_header // num_bytes
+[u4]8 // num_fields: |num_bytes| is too small for |num_fields|.
+[u4]0x80000000 // name
+[u4]0 // flags
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_bytes_num_fields_mismatch_3.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.data
new file mode 100644
index 0000000..a94e6ce
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.data
@@ -0,0 +1,6 @@
+[dist4]message_header // num_bytes
+[u4]1 // num_fields: Less than the minimal number of fields
+ // that we expect.
+[u4]0x80000000 // name
+[u4]0 // flags
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_num_fields_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data
new file mode 100644
index 0000000..1a5603e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.data
@@ -0,0 +1,11 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]1 // num_fields
+[f]-1 // param0
+[u4]0 // padding
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data
new file mode 100644
index 0000000..5556808
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.data
@@ -0,0 +1,9 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[u4]16 // num_bytes: Incomplete struct.
+[u4]1 // num_fields
+[f]-1 // param0
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data
new file mode 100644
index 0000000..9a362f6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.data
@@ -0,0 +1,7 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[u4]16 // num_bytes: Incomplete struct header.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_incomplete_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data
new file mode 100644
index 0000000..52e4540
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.data
@@ -0,0 +1,10 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[u4]0xFFFFFFFF // num_bytes: Test whether a huge value will cause overflow.
+[u4]1 // num_fields
+[f]-1 // param0
+[u4]0 // padding
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_huge.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data
new file mode 100644
index 0000000..7c966f8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.data
@@ -0,0 +1,9 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method0_params // num_bytes: Less than the minimal size that we expect.
+[u4]1 // num_fields
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data
new file mode 100644
index 0000000..af20941
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.data
@@ -0,0 +1,10 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[u4]4 // num_bytes: Less than the size of struct header.
+[u4]1 // num_fields
+[f]-1 // param0
+[u4]0 // padding
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_struct_num_bytes_less_than_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data
new file mode 100644
index 0000000..d67a72f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.data
@@ -0,0 +1,16 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]1 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method1_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method1_params
+
+[anchr]param0_ptr
+[dist4]struct_a // num_bytes
+[u4]1 // num_fields
+[u8]1234 // i
+[anchr]struct_a
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data
new file mode 100644
index 0000000..051dccc
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.data
@@ -0,0 +1,18 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]1 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method1_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method1_params
+
+[u1]0 // Causes the following struct to be misaligned.
+
+[anchr]param0_ptr
+[dist4]struct_a // num_bytes
+[u4]1 // num_fields
+[u8]1234 // i
+[anchr]struct_a
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected
new file mode 100644
index 0000000..acca999
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_misaligned_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MISALIGNED_OBJECT
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data
new file mode 100644
index 0000000..98a21c4
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.data
@@ -0,0 +1,11 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]1 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method1_params // num_bytes
+[u4]1 // num_fields
+[u8]0xFFFFFFFFFFFFFFFF // param0: Test whether decoding the pointer causes
+ // overflow.
+[anchr]method1_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected
new file mode 100644
index 0000000..23abb8c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_struct_pointer_overflow.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data
new file mode 100644
index 0000000..c53a78b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data
@@ -0,0 +1,10 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]1 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method1_params // num_bytes
+[u4]1 // num_fields
+[u8]0 // param0: An unexpected null pointer.
+[anchr]method1_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data
new file mode 100644
index 0000000..38e20ab
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]2 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method2_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b // num_bytes
+[u4]1 // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+[u8]0 // Having extra bytes in the middle is okay if the following objects are
+ // still properly alignmented.
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member // num_bytes
+[u4]1 // num_fields
+[u8]12345 // i
+[anchr]struct_a_member
+
+[anchr]param1_ptr
+[dist4]struct_a_param // num_bytes
+[u4]1 // num_fields
+[u8]67890 // i
+[anchr]struct_a_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data
new file mode 100644
index 0000000..a12121b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.data
@@ -0,0 +1,25 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]2 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method2_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b // num_bytes
+[u4]1 // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+// There are two pointers pointing to the same struct.
+[anchr]struct_a_ptr
+[anchr]param1_ptr
+[dist4]struct_a // num_bytes
+[u4]1 // num_fields
+[u8]12345 // i
+[anchr]struct_a
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_multiple_pointers_to_same_struct.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data
new file mode 100644
index 0000000..a916f03
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.data
@@ -0,0 +1,30 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]2 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method2_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b // num_bytes
+[u4]1 // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member // num_bytes
+[u4]1 // num_fields
+
+[anchr]param1_ptr
+// The following |num_bytes| and |num_fields| fields are also the |i| field
+// of the previous struct.
+[dist4]struct_a_param // num_bytes
+[u4]1 // num_fields
+[anchr]struct_a_member
+[u8]67890 // i
+[anchr]struct_a_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_overlapped_objects.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data
new file mode 100644
index 0000000..7145ccd
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.data
@@ -0,0 +1,32 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]2 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method2_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method2_params
+
+[anchr]param0_ptr
+[dist4]struct_b // num_bytes
+[u4]1 // num_fields
+[dist8]struct_a_ptr // struct_a
+[anchr]struct_b
+
+// The following two structs are arranged in wrong order.
+
+[anchr]param1_ptr
+[dist4]struct_a_param // num_bytes
+[u4]1 // num_fields
+[u8]67890 // i
+[anchr]struct_a_param
+
+[anchr]struct_a_ptr
+[dist4]struct_a_member // num_bytes
+[u4]1 // num_fields
+[u8]12345 // i
+[anchr]struct_a_member
+
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd2_wrong_layout_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data
new file mode 100644
index 0000000..ca7651a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.data
@@ -0,0 +1,16 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]0xFFFFFFFF // num_bytes: Test whether a huge value will cause overflow.
+[u4]12 // num_elements
+[b]01010101
+[b]00001111
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_huge.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data
new file mode 100644
index 0000000..9bc1fc3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.data
@@ -0,0 +1,16 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]7 // num_bytes: Less than the size of array header.
+[u4]12 // num_elements
+[b]01010101
+[b]00001111
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data
new file mode 100644
index 0000000..bed891c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.data
@@ -0,0 +1,18 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]array // num_bytes: Less than the size needed (array header + 12 boolean
+ // values).
+[u4]12 // num_elements
+[b]01010101
+[anchr]array
+[b]00001111
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_num_bytes_less_than_necessary_size.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data
new file mode 100644
index 0000000..f8d7644
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.data
@@ -0,0 +1,11 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[u8]0xFFFFFFFFFFFFFFFF // param0: Test whether decoding the pointer causes
+ // overflow.
+[anchr]method3_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected
new file mode 100644
index 0000000..23abb8c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_array_pointer_overflow.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data
new file mode 100644
index 0000000..cc7dd38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.data
@@ -0,0 +1,17 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]array // num_bytes
+[u4]12 // num_elements
+[b]01010101
+[b]00001111
+[anchr]array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data
new file mode 100644
index 0000000..7850893
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.data
@@ -0,0 +1,14 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]16 // num_bytes
+[u1]0 // num_elements: Incomplete array.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data
new file mode 100644
index 0000000..5de9fe6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.data
@@ -0,0 +1,13 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[u4]16 // num_bytes: Incomplete array header.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_incomplete_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data
new file mode 100644
index 0000000..40a13e0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.data
@@ -0,0 +1,19 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[u2]0 // Causes the following array to be misaligned.
+
+[anchr]param0_ptr
+[dist4]array // num_bytes
+[u4]12 // num_elements
+[b]01010101
+[b]00001111
+[anchr]array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected
new file mode 100644
index 0000000..acca999
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_misaligned_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MISALIGNED_OBJECT
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data
new file mode 100644
index 0000000..0edab4b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data
@@ -0,0 +1,10 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]3 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method3_params // num_bytes
+[u4]1 // num_fields
+[u8]0 // param0: An unexpected null pointer.
+[anchr]method3_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data
new file mode 100644
index 0000000..46f7502
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]4 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method4_params // num_bytes: Larger than what we know is okay.
+[u4]3 // num_fields: Larger than what we know is okay.
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[u8]0 // unknown
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c // num_bytes
+[u4]1 // num_fields
+[dist8]array_ptr // array
+[anchr]struct_c
+
+[anchr]array_ptr
+[dist4]array_member // num_bytes
+[u4]3 // num_elements
+0 1 2
+[anchr]array_member
+
+[u4]0 [u1]0 // Padding to make the next array aligned properly.
+
+[anchr]param1_ptr
+[dist4]array_param // num_bytes
+[u4]10 // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data
new file mode 100644
index 0000000..f3a0e9f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.data
@@ -0,0 +1,24 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]4 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method4_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c // num_bytes
+[u4]1 // num_fields
+[dist8]array_ptr // array
+[anchr]struct_c
+
+[anchr]param1_ptr
+[anchr]array_ptr
+[dist4]array_member // num_bytes
+[u4]3 // num_elements
+0 1 2
+[anchr]array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_multiple_pointers_to_same_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data
new file mode 100644
index 0000000..d819857
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.data
@@ -0,0 +1,30 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]4 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method4_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c // num_bytes
+[u4]1 // num_fields
+[dist8]array_ptr // array
+[anchr]struct_c
+
+[anchr]array_ptr
+[dist4]array_member // num_bytes
+[u4]3 // num_elements
+
+[anchr]param1_ptr
+// The first three bytes of |num_bytes| are also the elements of the previous
+// array.
+[dist4]array_param // num_bytes
+[u4]10 // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_param
+[anchr]array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_overlapped_objects.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data
new file mode 100644
index 0000000..fdc925f
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.data
@@ -0,0 +1,33 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]4 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method4_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method4_params
+
+[anchr]param0_ptr
+[dist4]struct_c // num_bytes
+[u4]1 // num_fields
+[dist8]array_ptr // array
+[anchr]struct_c
+
+// The following two arrays are arranged in wrong order.
+
+[anchr]param1_ptr
+[dist4]array_param // num_bytes
+[u4]10 // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_param
+
+[u4]0 [u2]0 // Padding to make the next array aligned properly.
+
+[anchr]array_ptr
+[dist4]array_member // num_bytes
+[u4]3 // num_elements
+0 1 2
+[anchr]array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected
new file mode 100644
index 0000000..779df88
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd4_wrong_layout_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data
new file mode 100644
index 0000000..0518c14
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.data
@@ -0,0 +1,35 @@
+[handles]10 // Larger than the number of handles that we know about is okay.
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]5 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method5_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[u4]4 // param1
+[u4]0 // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e // num_bytes
+[u4]2 // num_fields
+[dist8]struct_d_ptr // struct_d
+[u4]3 // data_pipe_consumer
+[u4]0 // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d // num_bytes
+[u4]1 // num_fields
+[dist8]message_pipes_ptr // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array // num_bytes
+[u4]2 // num_elements
+[u4]0
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data
new file mode 100644
index 0000000..43cad7c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data
@@ -0,0 +1,36 @@
+[handles]10
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]5 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method5_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[u4]10 // param1: It is outside of the valid encoded handle
+ // range [0, 10).
+[u4]0 // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e // num_bytes
+[u4]2 // num_fields
+[dist8]struct_d_ptr // struct_d
+[u4]3 // data_pipe_consumer
+[u4]0 // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d // num_bytes
+[u4]1 // num_fields
+[dist8]message_pipes_ptr // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array // num_bytes
+[u4]2 // num_elements
+[u4]0
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data
new file mode 100644
index 0000000..8b39c00
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data
@@ -0,0 +1,35 @@
+[handles]10
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]5 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method5_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[u4]4 // param1
+[u4]0 // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e // num_bytes
+[u4]2 // num_fields
+[dist8]struct_d_ptr // struct_d
+[u4]4 // data_pipe_consumer: The same value as |param1| above.
+[u4]0 // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d // num_bytes
+[u4]1 // num_fields
+[dist8]message_pipes_ptr // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array // num_bytes
+[u4]2 // num_elements
+[u4]0
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data
new file mode 100644
index 0000000..95a356b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.data
@@ -0,0 +1,35 @@
+[handles]10
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]5 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method5_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[u4]4 // param1
+[u4]0 // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e // num_bytes
+[u4]2 // num_fields
+[dist8]struct_d_ptr // struct_d
+[u4]3 // data_pipe_consumer
+[u4]0 // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d // num_bytes
+[u4]1 // num_fields
+[dist8]message_pipes_ptr // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array // num_bytes
+[u4]2 // num_elements
+[u4]1 // The two message pipe handles have the same value.
+[u4]1
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_2.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data
new file mode 100644
index 0000000..ef49285
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data
@@ -0,0 +1,34 @@
+[handles]5
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]5 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method5_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[u4]4 // param1
+[u4]0 // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e // num_bytes
+[u4]2 // num_fields
+[dist8]struct_d_ptr // struct_d
+[s4]-1 // data_pipe_consumer: An unexpected invalid handle.
+[u4]0 // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d // num_bytes
+[u4]1 // num_fields
+[dist8]message_pipes_ptr // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array // num_bytes
+[u4]1 // num_elements
+[u4]2
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected
new file mode 100644
index 0000000..6768236
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data
new file mode 100644
index 0000000..c6fa8ea
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.data
@@ -0,0 +1,36 @@
+[handles]10
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]5 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method5_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[u4]9 // param1
+[u4]0 // padding
+[anchr]method5_params
+
+[anchr]param0_ptr
+[dist4]struct_e // num_bytes
+[u4]2 // num_fields
+[dist8]struct_d_ptr // struct_d
+[u4]1 // data_pipe_consumer: It is smaller than those handles
+ // in |message_pipe_array|, which is wrong.
+[u4]0 // padding
+[anchr]struct_e
+
+[anchr]struct_d_ptr
+[dist4]struct_d // num_bytes
+[u4]1 // num_fields
+[dist8]message_pipes_ptr // message_pipes
+[anchr]struct_d
+
+[anchr]message_pipes_ptr
+[dist4]message_pipe_array // num_bytes
+[u4]2 // num_elements
+[u4]3
+[u4]4
+[anchr]message_pipe_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected
new file mode 100644
index 0000000..eef8e38
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_wrong_handle_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_HANDLE
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data
new file mode 100644
index 0000000..4f34c0b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.data
@@ -0,0 +1,22 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]6 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method6_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method6_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]1 // num_elements
+[dist8]element_ptr
+[anchr]array_param
+
+[anchr]element_ptr
+[dist4]array_element // num_bytes
+[u4]10 // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_element
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data
new file mode 100644
index 0000000..a58396c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.data
@@ -0,0 +1,22 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]6 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method6_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method6_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]1 // num_elements
+[dist8]element_ptr
+[anchr]array_param
+
+[anchr]element_ptr
+[dist4]array_element // num_bytes: It is insufficient to store 12 elements.
+[u4]12 // num_elements
+0 1 2 3 4 5 6 7 8 9
+[anchr]array_element
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd6_nested_array_num_bytes_less_than_necessary_size.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data
new file mode 100644
index 0000000..d6562af
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data
@@ -0,0 +1,32 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]7 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method7_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[u8]0 // unknown
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f // num_bytes
+[u4]1 // num_fields
+[dist8]array_ptr // fixed_size_array
+[anchr]struct_f
+
+[anchr]array_ptr
+[dist4]array_member // num_bytes
+[u4]3 // num_elements
+0 1 2
+[anchr]array_member
+
+[u4]0 [u1]0 // Padding to make the next array aligned properly.
+
+[anchr]param1_ptr
+[dist4]array_param // num_bytes
+[u4]5 // num_elements
+0 1 2 3 4
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data
new file mode 100644
index 0000000..bf1ddd5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data
@@ -0,0 +1,31 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]7 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method7_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f // num_bytes
+[u4]1 // num_fields
+[dist8]array_ptr // fixed_size_array
+[anchr]struct_f
+
+[anchr]array_ptr
+[dist4]array_member // num_bytes
+[u4]2 // num_elements: Too few elements.
+0 1
+[anchr]array_member
+
+[u4]0 [u1]0 [u1]0 // Padding for alignment of next array.
+
+[anchr]param1_ptr
+[dist4]array_param // num_bytes
+[u4]5 // num_elements
+0 1 2 3 4
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data
new file mode 100644
index 0000000..ab69175
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data
@@ -0,0 +1,23 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]7 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method7_params // num_bytes
+[u4]2 // num_fields
+[dist8]param0_ptr // param0
+[dist8]param1_ptr // param1
+[anchr]method7_params
+
+[anchr]param0_ptr
+[dist4]struct_f // num_bytes
+[u4]1 // num_fields
+[u8]0 // fixed_size_array: An unexpected null pointer.
+[anchr]struct_f
+
+[anchr]param1_ptr
+[dist4]array_param // num_bytes
+[u4]5 // num_elements
+0 1 2 3 4
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data
new file mode 100644
index 0000000..059e538
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.data
@@ -0,0 +1,19 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]8 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method8_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method8_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]0x20000001 // num_elements: The corresponding array size should be
+ // 0x20000001 * 8 + 8 = 0x100000010 which is
+ // 2^32 + 16 (base-10), while |num_bytes| is a 32-bit
+ // unsigned integer and its value is 16.
+[u8]0
+[anchr]array_param
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_array_num_bytes_overflow.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data
new file mode 100644
index 0000000..fd5e174
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data
@@ -0,0 +1,30 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]8 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method8_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method8_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]3 // num_elements
+[u8]0 // A null pointer, which is okay.
+[dist8]nested_array_ptr
+[u8]0 // A null pointer, which is okay.
+[anchr]array_param
+
+[anchr]nested_array_ptr
+[dist4]nested_array // num_bytes
+[u4]1 // num_elements
+[dist8]string_ptr
+[anchr]nested_array
+
+[anchr]string_ptr
+[dist4]string // num_bytes
+[u4]5 // num_elements
+0 1 2 3 4
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data
new file mode 100644
index 0000000..f1d8718
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data
@@ -0,0 +1,10 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]8 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method8_params // num_bytes
+[u4]1 // num_fields
+[u8]0 // param0: An unexpected null pointer.
+[anchr]method8_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data
new file mode 100644
index 0000000..104dddb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data
@@ -0,0 +1,31 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]8 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method8_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method8_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]3 // num_elements
+[u8]0 // A null pointer, which is okay.
+[dist8]nested_array_ptr
+[u8]0 // A null pointer, which is okay.
+[anchr]array_param
+
+[anchr]nested_array_ptr
+[dist4]nested_array // num_bytes
+[u4]2 // num_elements
+[dist8]string_ptr
+[u8]0 // An unexpected null pointer.
+[anchr]nested_array
+
+[anchr]string_ptr
+[dist4]string // num_bytes
+[u4]5 // num_elements
+0 1 2 3 4
+[anchr]string
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data
new file mode 100644
index 0000000..c548906
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data
@@ -0,0 +1,34 @@
+[handles]4
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]9 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method9_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method9_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]2 // num_elements
+[dist8]nested_array_ptr_0
+[dist8]nested_array_ptr_1
+[anchr]array_param
+
+[anchr]nested_array_ptr_0
+[dist4]nested_array_0 // num_bytes
+[u4]2 // num_elements
+[u4]0
+[s4]-1 // An invalid handle, which is okay.
+[anchr]nested_array_0
+
+[anchr]nested_array_ptr_1
+[dist4]nested_array_1 // num_bytes
+[u4]3 // num_elements
+[u4]2
+[s4]-1 // An invalid handle, which is okay.
+[u4]3
+[anchr]nested_array_1
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data
new file mode 100644
index 0000000..f0967d5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data
@@ -0,0 +1,12 @@
+[handles]4
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]9 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method9_params // num_bytes
+[u4]1 // num_fields
+[u8]0 // param0: A null pointer, which is okay.
+[anchr]method9_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data
new file mode 100644
index 0000000..9c8f478
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data
@@ -0,0 +1,25 @@
+[handles]4
+
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]9 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method9_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method9_params
+
+[anchr]param0_ptr
+[dist4]array_param // num_bytes
+[u4]2 // num_elements
+[dist8]nested_array_ptr_0
+[u8]0 // An unexpected null pointer.
+[anchr]array_param
+
+[anchr]nested_array_ptr_0
+[dist4]nested_array_0 // num_bytes
+[u4]1 // num_elements
+[u4]0
+[anchr]nested_array_0
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected
new file mode 100644
index 0000000..95d8db0
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_NULL_POINTER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data
new file mode 100644
index 0000000..df7b7e8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.data
@@ -0,0 +1,17 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]basic_struct // num_bytes
+[u4]1 // num_fields
+[s4]-1 // a
+[u4]0 // padding
+[anchr]basic_struct
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data
new file mode 100644
index 0000000..3dcca9d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.data
@@ -0,0 +1,14 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[u4]0 // num_bytes: The struct size is too small.
+[u4]0 // num_fields
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf1_mthd0_rqst_unexpected_struct_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data
new file mode 100644
index 0000000..f4b6c40
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.data
@@ -0,0 +1,17 @@
+[dist4]message_header // num_bytes
+[u4]3 // num_fields
+[u4]0 // name
+[u4]2 // flags: Is response.
+[u8]1 // request_id
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]uint8_array // num_bytes
+[u4]1 // num_elements
+[u1]0
+[anchr]uint8_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data
new file mode 100644
index 0000000..a357013
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.data
@@ -0,0 +1,17 @@
+[dist4]message_header // num_bytes
+[u4]3 // num_fields
+[u4]0 // name
+[u4]2 // flags: Is response.
+[u8]1 // request_id
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]1 // num_fields
+[dist8]param0_ptr // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]uint8_array // num_bytes
+[u4]2 // num_elements: The size is too small to hold 2 elements.
+[u1]0
+[anchr]uint8_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected
new file mode 100644
index 0000000..5a1ec4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_intf2_mthd0_resp_unexpected_array_header.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data
new file mode 100644
index 0000000..6d2cb4e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.data
@@ -0,0 +1,6 @@
+[dist4]message_header // num_bytes
+[u4]3 // num_fields
+[u4]0xffffffff // name
+[u4]3 // flags: This combination is illegal.
+[u8]1 // request_id
+[anchr]message_header
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected
new file mode 100644
index 0000000..696c78d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/integration_msghdr_invalid_flags.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.data b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.data
new file mode 100644
index 0000000..ffcafd3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.data
@@ -0,0 +1,12 @@
+[dist4]message_header // num_bytes
+[u4]2 // num_fields
+[u4]0 // name
+[u4]0 // flags
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]0 // num_fields: Less than the minimal number of fields
+ // that we expect.
+[f]-1 // param0
+[u4]0 // padding
+[anchr]method0_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.expected b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.expected
new file mode 100644
index 0000000..25aceee
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/not_implemented_mthd0_struct_num_fields_less_than_min_requirement.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER
diff --git a/mojo/public/interfaces/bindings/tests/math_calculator.mojom b/mojo/public/interfaces/bindings/tests/math_calculator.mojom
new file mode 100644
index 0000000..a18add1
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/math_calculator.mojom
@@ -0,0 +1,19 @@
+// 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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.math"]
+module math {
+
+[Client=CalculatorUI]
+interface Calculator {
+ Clear@0();
+ Add@1(double value@0);
+ Multiply@2(double value@0);
+};
+
+interface CalculatorUI {
+ Output@0(double value@0);
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/no_module.mojom b/mojo/public/interfaces/bindings/tests/no_module.mojom
new file mode 100644
index 0000000..f380011
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/no_module.mojom
@@ -0,0 +1,9 @@
+// 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.
+
+// Entities without module
+
+enum EnumWithoutModule {
+ A
+};
diff --git a/mojo/public/interfaces/bindings/tests/rect.mojom b/mojo/public/interfaces/bindings/tests/rect.mojom
new file mode 100644
index 0000000..324ce48
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/rect.mojom
@@ -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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"]
+module mojo.test {
+
+struct Rect {
+ int32 x;
+ int32 y;
+ int32 width;
+ int32 height;
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/regression_tests.mojom b/mojo/public/interfaces/bindings/tests/regression_tests.mojom
new file mode 100644
index 0000000..49ab69a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/regression_tests.mojom
@@ -0,0 +1,37 @@
+// 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.
+
+// Module containing entities for regression tests of the generator. Entities
+// must never be modified, instead new entity must be added to add new tests.
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.regression_tests"]
+module regression_tests {
+
+interface CheckMethodWithEmptyResponse {
+WithouParameterAndEmptyResponse() => ();
+WithParameterAndEmptyResponse(bool b) => ();
+};
+
+interface CheckNameCollision {
+WithNameCollision(bool message, bool response) => (bool message, bool response);
+};
+
+enum EnumWithReference {
+ k_STEREO_AND_KEYBOARD_MIC = 30,
+ k_MAX = k_STEREO_AND_KEYBOARD_MIC
+};
+
+enum EnumWithLowercase {
+ PlanarF16,
+ PlanarF32
+};
+
+enum EnumWithNumbers {
+ k_2_1 = 4
+};
+
+enum EnumWithK {
+ K = 0
+};
+
+} // module imported
diff --git a/mojo/public/interfaces/bindings/tests/sample_factory.mojom b/mojo/public/interfaces/bindings/tests/sample_factory.mojom
new file mode 100644
index 0000000..567cf23
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_factory.mojom
@@ -0,0 +1,48 @@
+// 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.
+
+import "sample_import.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample"]
+module sample {
+
+// This sample shows how handles to MessagePipes can be sent as both parameters
+// to methods as well as fields on structs.
+
+struct Request {
+ int32 x;
+ handle<message_pipe>? pipe;
+ array<handle<message_pipe>>? more_pipes;
+
+ // Interfaces can be used as members.
+ imported.ImportedInterface? obj;
+};
+
+struct Response {
+ int32 x;
+ handle<message_pipe>? pipe;
+};
+
+interface NamedObject {
+ SetName(string name);
+ GetName() => (string name);
+};
+
+[Client=FactoryClient]
+interface Factory {
+ DoStuff(Request request, handle<message_pipe>? pipe);
+ DoStuff2(handle<data_pipe_consumer> pipe);
+ CreateNamedObject(NamedObject& obj);
+ RequestImportedInterface(
+ imported.ImportedInterface& obj) => (imported.ImportedInterface& obj);
+ TakeImportedInterface(
+ imported.ImportedInterface obj) => (imported.ImportedInterface obj);
+};
+
+interface FactoryClient {
+ DidStuff(Response response, string text);
+ DidStuff2(string text);
+};
+
+} // module sample
diff --git a/mojo/public/interfaces/bindings/tests/sample_import.mojom b/mojo/public/interfaces/bindings/tests/sample_import.mojom
new file mode 100644
index 0000000..659b74d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_import.mojom
@@ -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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.imported"]
+module imported {
+
+// This sample just defines some types that are imported into
+// sample_service.mojom, to show how import works.
+
+enum Shape {
+ RECTANGLE = 1,
+ CIRCLE,
+ TRIANGLE,
+ LAST = TRIANGLE,
+};
+
+// These enum values should not interfere with those of Shape above.
+enum AnotherShape {
+ RECTANGLE = 10,
+ CIRCLE,
+ TRIANGLE,
+};
+
+enum YetAnotherShape {
+ RECTANGLE = 20,
+ CIRCLE,
+ TRIANGLE,
+};
+
+struct Point {
+ int32 x;
+ int32 y;
+};
+
+interface ImportedInterface {
+ DoSomething();
+};
+
+} // module imported
diff --git a/mojo/public/interfaces/bindings/tests/sample_import2.mojom b/mojo/public/interfaces/bindings/tests/sample_import2.mojom
new file mode 100644
index 0000000..63ddb5a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_import2.mojom
@@ -0,0 +1,30 @@
+// 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 "sample_import.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.imported"]
+module imported {
+
+// This sample adds more types and constants to the "imported" namespace,
+// to test a bug with importing multiple modules with the same namespace.
+
+enum Color {
+ RED,
+ BLACK,
+};
+
+struct Size {
+ int32 width;
+ int32 height;
+};
+
+struct Thing {
+ imported.Shape shape = RECTANGLE;
+ int32 color = Color.BLACK;
+ Point location;
+ Size size;
+};
+
+} // module imported
diff --git a/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom b/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom
new file mode 100644
index 0000000..01e78ed
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom
@@ -0,0 +1,28 @@
+// 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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample",
+ JavaConstantsClassName="InterfaceConstants",
+ Foo = "hello world"]
+module sample {
+
+const uint64 kLong = 4405;
+
+enum Enum {
+ VALUE
+};
+
+[Client=ProviderClient]
+interface Provider {
+ EchoString(string a) => (string a);
+ EchoStrings(string a, string b) => (string a, string b);
+ EchoMessagePipeHandle(handle<message_pipe> a) => (handle<message_pipe> a);
+ EchoEnum(Enum a) => (Enum a);
+};
+
+// TODO(darin): We shouldn't need this, but JS bindings don't work without it.
+interface ProviderClient {
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/sample_service.mojom b/mojo/public/interfaces/bindings/tests/sample_service.mojom
new file mode 100644
index 0000000..baf03de
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/sample_service.mojom
@@ -0,0 +1,128 @@
+// 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.
+
+import "sample_import.mojom"
+import "sample_import2.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.sample"]
+module sample {
+
+const uint8 kTwelve = 12;
+const uint64 kTooBigForSignedInt64 = 9999999999999999999;
+
+const double kDoubleInfinity = double.INFINITY;
+const double kDoubleNegativeInfinity = double.NEGATIVE_INFINITY;
+const double kDoubleNaN = double.NAN;
+
+const float kFloatInfinity = float.INFINITY;
+const float kFloatNegativeInfinity = float.NEGATIVE_INFINITY;
+const float kFloatNaN = float.NAN;
+
+struct Bar {
+ enum Type {
+ VERTICAL = 1,
+ HORIZONTAL,
+ BOTH,
+ INVALID
+ };
+ uint8 alpha@0 = 0xff;
+ uint8 beta@1;
+ uint8 gamma@2;
+ Type type@3 = sample.Bar.Type.VERTICAL;
+};
+
+[RequiredFields=7]
+struct Foo {
+ const string kFooby = "Fooby";
+ string name@8 = kFooby;
+ int32 x@0;
+ int32 y@1;
+ bool a@2 = true;
+ bool b@3;
+ bool c@4;
+ Bar? bar@5;
+ array<Bar>? extra_bars@7;
+ array<uint8>? data@6;
+ handle<message_pipe>? source@9;
+ array<handle<data_pipe_consumer>>? input_streams@10;
+ array<handle<data_pipe_producer>>? output_streams@11;
+ array<array<bool>>? array_of_array_of_bools@12;
+ array<array<array<string>>>? multi_array_of_strings@13;
+ array<bool>? array_of_bools@14;
+};
+
+struct DefaultsTest {
+ int8 a0@0 = -12;
+ uint8 a1@1 = sample.kTwelve;
+ int16 a2@2 = 1234;
+ uint16 a3@3 = 34567;
+ int32 a4@4 = 123456;
+ uint32 a5@5 = 3456789012;
+ int64 a6@6 = -111111111111;
+ uint64 a7@7 = 9999999999999999999;
+ int32 a8@8 = 0x12345;
+ int32 a9@9 = -0x12345;
+ int32 a10@10 = +1234;
+ bool a11@11 = true;
+ bool a12@12 = false;
+ float a13@13 = 123.25;
+ double a14@14 = 1234567890.123;
+ double a15@15 = 1E10;
+ double a16@16 = -1.2E+20;
+ double a17@17 = +1.23E-20;
+
+ // TODO(vtl): Add tests for default vs null when those are implemented (for
+ // structs, arrays, and strings).
+ array<uint8> a18@18;
+ string a19@19;
+
+ Bar.Type a20@20 = BOTH;
+ imported.Point a21@21;
+ imported.Thing a22@22 = default;
+
+ uint64 a23@23 = 0xFFFFFFFFFFFFFFFF;
+ int64 a24@24 = 0x123456789;
+ int64 a25@25 = -0x123456789;
+
+ double a26@26 = double.INFINITY;
+ double a27@27 = double.NEGATIVE_INFINITY;
+ double a28@28 = double.NAN;
+ float a29@29 = float.INFINITY;
+ float a30@30 = float.NEGATIVE_INFINITY;
+ float a31@31 = float.NAN;
+};
+
+struct StructWithHoleV1 {
+ int32 v1 = 1;
+ int64 v2 = 2;
+};
+
+struct StructWithHoleV2 {
+ int32 v1 = 1;
+ int64 v2 = 2;
+ int32 v3 = 3;
+};
+
+[Client=ServiceClient]
+interface Service {
+ enum BazOptions {
+ REGULAR = 0,
+ EXTRA
+ };
+ const uint8 kFavoriteBaz = 1;
+ Frobinate@0(Foo? foo@0, BazOptions baz@1, Port? port@2);
+ GetPort@1(Port& port @0);
+};
+
+interface ServiceClient {
+ DidFrobinate@0(int32 result@0);
+};
+
+// This interface is referenced above where it is defined. It also refers to
+// itself from a method.
+interface Port {
+ PostMessage@0(string message_text@0, Port port@1);
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom b/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom
new file mode 100644
index 0000000..f51c197
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom
@@ -0,0 +1,38 @@
+// 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.
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.mojo"]
+module mojo.test {
+
+struct Struct1 {
+ uint8 i;
+};
+
+struct Struct2 {
+ handle hdl;
+};
+
+struct Struct3 {
+ Struct1 struct_1;
+};
+
+struct Struct4 {
+ array<Struct1> data;
+};
+
+struct Struct5 {
+ array<Struct1, 2> pair;
+};
+
+struct Struct6 {
+ string str;
+};
+
+struct StructOfNullables {
+ handle? hdl;
+ Struct1? struct_1;
+ string? str;
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/test_structs.mojom b/mojo/public/interfaces/bindings/tests/test_structs.mojom
new file mode 100644
index 0000000..d361da7
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom
@@ -0,0 +1,85 @@
+// 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.
+
+import "mojo/public/interfaces/bindings/tests/rect.mojom"
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.test_structs"]
+module mojo.test {
+
+struct NamedRegion {
+ string? name;
+ array<Rect>? rects;
+};
+
+struct RectPair {
+ Rect? first;
+ Rect? second;
+};
+
+struct EmptyStruct {
+};
+
+// Used to verify that struct fields which don't specify a deafult are
+// initialized to: false for bool, 0 for numbers, and null for strings,
+// handles, and structs. The "?" nullable suffix shouldn't have any
+// impact on initial field values.
+
+struct NoDefaultFieldValues {
+ bool f0;
+ int8 f1;
+ uint8 f2;
+ int16 f3;
+ uint16 f4;
+ int32 f5;
+ uint32 f6;
+ int64 f7;
+ uint64 f8;
+ float f9;
+ double f10;
+ string f11;
+ string? f12;
+ handle<message_pipe> f13;
+ handle<data_pipe_consumer> f14;
+ handle<data_pipe_producer> f15;
+ handle<message_pipe>? f16;
+ handle<data_pipe_consumer>? f17;
+ handle<data_pipe_producer>? f18;
+ handle f19;
+ handle? f20;
+ handle<shared_buffer> f21;
+ handle<shared_buffer>? f22;
+ array<string> f23;
+ array<string?> f24;
+ array<string>? f25;
+ array<string?>? f26;
+ EmptyStruct f27;
+ EmptyStruct? f28;
+};
+
+// Used to verify that struct fields with an explicit default value
+// are initialized correctly. The "?" nullable suffix shouldn't have any
+// impact on initial field values.
+
+struct DefaultFieldValues {
+ const string kFoo = "foo";
+ bool f0 = true;
+ int8 f1 = 100;
+ uint8 f2 = 100;
+ int16 f3 = 100;
+ uint16 f4 = 100;
+ int32 f5 = 100;
+ uint32 f6 = 100;
+ int64 f7 = 100;
+ uint64 f8 = 100;
+ float f9 = 100;
+ float f10 = 100.0;
+ double f11 = 100;
+ double f12 = 100.0;
+ string f13 = kFoo;
+ string? f14 = kFoo;
+ Rect f15 = default;
+ Rect? f16 = default;
+};
+
+}
diff --git a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
new file mode 100644
index 0000000..5cd9d0b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
@@ -0,0 +1,61 @@
+// 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.
+
+
+[JavaPackage="org.chromium.mojo.bindings.test.mojom.mojo"]
+module mojo.test {
+
+struct StructA {
+ uint64 i;
+};
+
+struct StructB {
+ StructA struct_a;
+};
+
+struct StructC {
+ array<uint8> data;
+};
+
+struct StructD {
+ array<handle<message_pipe>> message_pipes;
+};
+
+struct StructE {
+ StructD struct_d;
+ handle<data_pipe_consumer> data_pipe_consumer;
+};
+
+struct StructF {
+ array<uint8, 3> fixed_size_array;
+};
+
+interface ConformanceTestInterface {
+ Method0(float param0);
+ Method1(StructA param0);
+ Method2(StructB param0, StructA param1);
+ Method3(array<bool> param0);
+ Method4(StructC param0, array<uint8> param1);
+ Method5(StructE param0, handle<data_pipe_producer> param1);
+ Method6(array<array<uint8>> param0);
+ Method7(StructF param0, array<uint8, 5> param1);
+ Method8(array<array<string>?> param0);
+ Method9(array<array<handle?>>? param0);
+};
+
+struct BasicStruct {
+ int32 a;
+};
+
+[Client=IntegrationTestInterface2]
+interface IntegrationTestInterface1 {
+ Method0(BasicStruct param0);
+};
+
+[Client=IntegrationTestInterface1]
+interface IntegrationTestInterface2 {
+ Method0() => (array<uint8> param0);
+};
+
+}
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
new file mode 100644
index 0000000..6f5d8e9
--- /dev/null
+++ b/mojo/public/java/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+android_library("system") {
+ java_files = [
+ "system/src/org/chromium/mojo/system/AsyncWaiter.java",
+ "system/src/org/chromium/mojo/system/Core.java",
+ "system/src/org/chromium/mojo/system/DataPipe.java",
+ "system/src/org/chromium/mojo/system/Flags.java",
+ "system/src/org/chromium/mojo/system/Handle.java",
+ "system/src/org/chromium/mojo/system/InvalidHandle.java",
+ "system/src/org/chromium/mojo/system/MessagePipeHandle.java",
+ "system/src/org/chromium/mojo/system/MojoException.java",
+ "system/src/org/chromium/mojo/system/MojoResult.java",
+ "system/src/org/chromium/mojo/system/Pair.java",
+ "system/src/org/chromium/mojo/system/SharedBufferHandle.java",
+ "system/src/org/chromium/mojo/system/UntypedHandle.java",
+ ]
+}
+
+android_library("bindings") {
+ java_files = [
+ "bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java",
+ "bindings/src/org/chromium/mojo/bindings/BindingsHelper.java",
+ "bindings/src/org/chromium/mojo/bindings/Callbacks.java",
+ "bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java",
+ "bindings/src/org/chromium/mojo/bindings/Connector.java",
+ "bindings/src/org/chromium/mojo/bindings/Decoder.java",
+ "bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java",
+ "bindings/src/org/chromium/mojo/bindings/DeserializationException.java",
+ "bindings/src/org/chromium/mojo/bindings/Encoder.java",
+ "bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java",
+ "bindings/src/org/chromium/mojo/bindings/HandleOwner.java",
+ "bindings/src/org/chromium/mojo/bindings/Interface.java",
+ "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java",
+ "bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java",
+ "bindings/src/org/chromium/mojo/bindings/MessageHeader.java",
+ "bindings/src/org/chromium/mojo/bindings/Message.java",
+ "bindings/src/org/chromium/mojo/bindings/MessageReceiver.java",
+ "bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java",
+ "bindings/src/org/chromium/mojo/bindings/RouterImpl.java",
+ "bindings/src/org/chromium/mojo/bindings/Router.java",
+ "bindings/src/org/chromium/mojo/bindings/SerializationException.java",
+ "bindings/src/org/chromium/mojo/bindings/ServiceMessage.java",
+ "bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java",
+ "bindings/src/org/chromium/mojo/bindings/Struct.java",
+ ]
+
+ deps = [ ":system" ]
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
new file mode 100644
index 0000000..8a83be9
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
@@ -0,0 +1,116 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper around {@link Router} that will close the connection when not referenced anymore.
+ */
+class AutoCloseableRouter implements Router {
+
+ /**
+ * The underlying router.
+ */
+ private final Router mRouter;
+
+ /**
+ * The executor to close the underlying router.
+ */
+ private final Executor mExecutor;
+
+ /**
+ * Flags to keep track if this router has been correctly closed.
+ */
+ private boolean mClosed;
+
+ /**
+ * Constructor.
+ */
+ public AutoCloseableRouter(Core core, Router router) {
+ mRouter = router;
+ mExecutor = ExecutorFactory.getExecutorForCurrentThread(core);
+ }
+
+ /**
+ * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
+ */
+ @Override
+ public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
+ mRouter.setIncomingMessageReceiver(incomingMessageReceiver);
+ }
+
+ /**
+ * @see HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ return mRouter.passHandle();
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ return mRouter.accept(message);
+ }
+
+ /**
+ * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+ */
+ @Override
+ public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+ return mRouter.acceptWithResponder(message, responder);
+
+ }
+
+ /**
+ * @see Router#start()
+ */
+ @Override
+ public void start() {
+ mRouter.start();
+ }
+
+ /**
+ * @see Router#setErrorHandler(ConnectionErrorHandler)
+ */
+ @Override
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ mRouter.setErrorHandler(errorHandler);
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ mRouter.close();
+ mClosed = true;
+ }
+
+ /**
+ * @see Object#finalize()
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ if (!mClosed) {
+ mExecutor.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ close();
+ }
+ });
+ throw new IllegalStateException("Warning: Router objects should be explicitly closed " +
+ "when no longer required otherwise you may leak handles.");
+ }
+ super.finalize();
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
new file mode 100644
index 0000000..b18e5c5
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.Handle;
+
+/**
+ * Helper functions.
+ */
+public class BindingsHelper {
+ /**
+ * Alignment in bytes for mojo serialization.
+ */
+ public static final int ALIGNMENT = 8;
+
+ /**
+ * The size, in bytes, of a serialized handle. A handle is serialized as an int representing the
+ * offset of the handle in the list of handles.
+ */
+ public static final int SERIALIZED_HANDLE_SIZE = 4;
+
+ /**
+ * The size, in bytes, of a serialized pointer. A pointer is serializaed as an unsigned long
+ * representing the offset from its position to the pointed elemnt.
+ */
+ public static final int POINTER_SIZE = 8;
+
+ /**
+ * The value used for the expected length of a non-fixed size array.
+ */
+ public static final int UNSPECIFIED_ARRAY_LENGTH = -1;
+
+ /**
+ * Align |size| on {@link BindingsHelper#ALIGNMENT}.
+ */
+ public static int align(int size) {
+ return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+ }
+
+ /**
+ * Passed as |arrayNullability| when neither the array nor its elements are nullable.
+ */
+ public static final int NOTHING_NULLABLE = 0;
+
+ /**
+ * "Array bit" of |arrayNullability| is set iff the array itself is nullable.
+ */
+ public static final int ARRAY_NULLABLE = (1 << 0);
+
+ /**
+ * "Element bit" of |arrayNullability| is set iff the array elements are nullable.
+ */
+ public static final int ELEMENT_NULLABLE = (1 << 1);
+
+ public static boolean isArrayNullable(int arrayNullability) {
+ return (arrayNullability & ARRAY_NULLABLE) > 0;
+ }
+
+ public static boolean isElementNullable(int arrayNullability) {
+ return (arrayNullability & ELEMENT_NULLABLE) > 0;
+ }
+
+ /**
+ * Align |size| on {@link BindingsHelper#ALIGNMENT}.
+ */
+ public static long align(long size) {
+ return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+ }
+
+ /**
+ * Compute the size in bytes of the given string encoded as utf8.
+ */
+ public static int utf8StringSizeInBytes(String s) {
+ int res = 0;
+ for (int i = 0; i < s.length(); ++i) {
+ char c = s.charAt(i);
+ int codepoint = c;
+ if (isSurrogate(c)) {
+ i++;
+ char c2 = s.charAt(i);
+ codepoint = Character.toCodePoint(c, c2);
+ }
+ res += 1;
+ if (codepoint > 0x7f) {
+ res += 1;
+ if (codepoint > 0x7ff) {
+ res += 1;
+ if (codepoint > 0xffff) {
+ res += 1;
+ if (codepoint > 0x1fffff) {
+ res += 1;
+ if (codepoint > 0x3ffffff) {
+ res += 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Determines if the given {@code char} value is a Unicode <i>surrogate code unit</i>. See
+ * {@link Character#isSurrogate}. Extracting here because the method only exists at API level
+ * 19.
+ */
+ private static boolean isSurrogate(char c) {
+ return c >= Character.MIN_SURROGATE && c < (Character.MAX_SURROGATE + 1);
+ }
+
+ /**
+ * Returns an {@link AsyncWaiter} to use with the given handle, or <code>null</code> if none if
+ * available.
+ */
+ static AsyncWaiter getDefaultAsyncWaiterForHandle(Handle handle) {
+ if (handle.getCore() != null) {
+ return handle.getCore().getDefaultAsyncWaiter();
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java
new file mode 100644
index 0000000..c6b14c1
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Callbacks.java
@@ -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.
+
+// This file was generated using
+// mojo/tools/generate_java_callback_interfaces.py
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Contains a generic interface for callbacks.
+ */
+public interface Callbacks {
+
+ /**
+ * A generic callback.
+ */
+ interface Callback0 {
+ /**
+ * Call the callback.
+ */
+ public void call();
+ }
+
+ /**
+ * A generic 1-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ */
+ interface Callback1<T1> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1);
+ }
+
+ /**
+ * A generic 2-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ */
+ interface Callback2<T1, T2> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2);
+ }
+
+ /**
+ * A generic 3-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ */
+ interface Callback3<T1, T2, T3> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3);
+ }
+
+ /**
+ * A generic 4-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ */
+ interface Callback4<T1, T2, T3, T4> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+ }
+
+ /**
+ * A generic 5-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ * @param <T5> the type of argument 5.
+ */
+ interface Callback5<T1, T2, T3, T4, T5> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
+ }
+
+ /**
+ * A generic 6-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ * @param <T5> the type of argument 5.
+ * @param <T6> the type of argument 6.
+ */
+ interface Callback6<T1, T2, T3, T4, T5, T6> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
+ }
+
+ /**
+ * A generic 7-argument callback.
+ *
+ * @param <T1> the type of argument 1.
+ * @param <T2> the type of argument 2.
+ * @param <T3> the type of argument 3.
+ * @param <T4> the type of argument 4.
+ * @param <T5> the type of argument 5.
+ * @param <T6> the type of argument 6.
+ * @param <T7> the type of argument 7.
+ */
+ interface Callback7<T1, T2, T3, T4, T5, T6, T7> {
+ /**
+ * Call the callback.
+ */
+ public void call(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
new file mode 100644
index 0000000..abb0bdf
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MojoException;
+
+/**
+ * A {@link ConnectionErrorHandler} is notified of an error happening while using the bindings over
+ * message pipes.
+ */
+interface ConnectionErrorHandler {
+ public void onConnectionError(MojoException e);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
new file mode 100644
index 0000000..cbb1924
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -0,0 +1,241 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A {@link Connector} owns a {@link MessagePipeHandle} and will send any received messages to the
+ * registered {@link MessageReceiver}. It also acts as a {@link MessageReceiver} and will send any
+ * message through the handle.
+ * <p>
+ * The method |start| must be called before the {@link Connector} will start listening to incoming
+ * messages.
+ */
+public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle> {
+
+ /**
+ * The callback that is notified when the state of the owned handle changes.
+ */
+ private final AsyncWaiterCallback mAsyncWaiterCallback = new AsyncWaiterCallback();
+
+ /**
+ * The owned message pipe.
+ */
+ private final MessagePipeHandle mMessagePipeHandle;
+
+ /**
+ * A waiter which is notified when a new message is available on the owned message pipe.
+ */
+ private final AsyncWaiter mAsyncWaiter;
+
+ /**
+ * The {@link MessageReceiver} to which received messages are sent.
+ */
+ private MessageReceiver mIncomingMessageReceiver;
+
+ /**
+ * The Cancellable for the current wait. Is |null| when not currently waiting for new messages.
+ */
+ private AsyncWaiter.Cancellable mCancellable;
+
+ /**
+ * The error handler to notify of errors.
+ */
+ private ConnectionErrorHandler mErrorHandler;
+
+ /**
+ * Create a new connector over a |messagePipeHandle|. The created connector will use the default
+ * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|.
+ */
+ public Connector(MessagePipeHandle messagePipeHandle) {
+ this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
+ }
+
+ /**
+ * Create a new connector over a |messagePipeHandle| using the given {@link AsyncWaiter} to get
+ * notified of changes on the handle.
+ */
+ public Connector(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+ mCancellable = null;
+ mMessagePipeHandle = messagePipeHandle;
+ mAsyncWaiter = asyncWaiter;
+ }
+
+ /**
+ * Set the {@link MessageReceiver} that will receive message from the owned message pipe.
+ */
+ public void setIncomingMessageReceiver(MessageReceiver incomingMessageReceiver) {
+ mIncomingMessageReceiver = incomingMessageReceiver;
+ }
+
+ /**
+ * Set the {@link ConnectionErrorHandler} that will be notified of errors on the owned message
+ * pipe.
+ */
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ mErrorHandler = errorHandler;
+ }
+
+ /**
+ * Start listening for incoming messages.
+ */
+ public void start() {
+ assert mCancellable == null;
+ registerAsyncWaiterForRead();
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ try {
+ mMessagePipeHandle.writeMessage(message.getData(),
+ message.getHandles(), MessagePipeHandle.WriteFlags.NONE);
+ return true;
+ } catch (MojoException e) {
+ onError(e);
+ return false;
+ }
+ }
+
+ /**
+ * Pass the owned handle of the connector. After this, the connector is disconnected. It cannot
+ * accept new message and it isn't listening to the handle anymore.
+ *
+ * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ cancelIfActive();
+ MessagePipeHandle handle = mMessagePipeHandle.pass();
+ if (mIncomingMessageReceiver != null) {
+ mIncomingMessageReceiver.close();
+ }
+ return handle;
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ cancelIfActive();
+ mMessagePipeHandle.close();
+ if (mIncomingMessageReceiver != null) {
+ mIncomingMessageReceiver.close();
+ }
+ }
+
+ private class AsyncWaiterCallback implements AsyncWaiter.Callback {
+
+ /**
+ * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ Connector.this.onAsyncWaiterResult(result);
+ }
+
+ /**
+ * @see org.chromium.mojo.system.AsyncWaiter.Callback#onError(MojoException)
+ */
+ @Override
+ public void onError(MojoException exception) {
+ Connector.this.onError(exception);
+ }
+
+ }
+
+ /**
+ * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
+ */
+ private void onAsyncWaiterResult(int result) {
+ mCancellable = null;
+ if (result == MojoResult.OK) {
+ readOutstandingMessages();
+ } else {
+ onError(new MojoException(result));
+ }
+ }
+
+ private void onError(MojoException exception) {
+ mCancellable = null;
+ close();
+ if (mErrorHandler != null) {
+ mErrorHandler.onConnectionError(exception);
+ }
+ }
+
+ /**
+ * Register to be called back when a new message is available on the owned message pipe.
+ */
+ private void registerAsyncWaiterForRead() {
+ assert mCancellable == null;
+ if (mAsyncWaiter != null) {
+ mCancellable = mAsyncWaiter.asyncWait(mMessagePipeHandle, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, mAsyncWaiterCallback);
+ } else {
+ onError(new MojoException(MojoResult.INVALID_ARGUMENT));
+ }
+ }
+
+ /**
+ * Read all available messages on the owned message pipe.
+ */
+ private void readOutstandingMessages() {
+ int result;
+ do {
+ try {
+ result = readAndDispatchMessage(mMessagePipeHandle, mIncomingMessageReceiver);
+ } catch (MojoException e) {
+ onError(e);
+ return;
+ }
+ } while (result == MojoResult.OK);
+ if (result == MojoResult.SHOULD_WAIT) {
+ registerAsyncWaiterForRead();
+ } else {
+ onError(new MojoException(result));
+ }
+ }
+
+ private void cancelIfActive() {
+ if (mCancellable != null) {
+ mCancellable.cancel();
+ mCancellable = null;
+ }
+ }
+
+ /**
+ * Read a message, and pass it to the given |MessageReceiver| if not null. If the
+ * |MessageReceiver| is null, the message is lost.
+ *
+ * @param receiver The {@link MessageReceiver} that will receive the read {@link Message}. Can
+ * be <code>null</code>, in which case the message is discarded.
+ */
+ static int readAndDispatchMessage(MessagePipeHandle handle, MessageReceiver receiver) {
+ // TODO(qsr) Allow usage of a pool of pre-allocated buffer for performance.
+ ReadMessageResult result = handle.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE);
+ if (result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) {
+ return result.getMojoResult();
+ }
+ ByteBuffer buffer = ByteBuffer.allocateDirect(result.getMessageSize());
+ result = handle.readMessage(buffer, result.getHandlesCount(),
+ MessagePipeHandle.ReadFlags.NONE);
+ if (receiver != null && result.getMojoResult() == MojoResult.OK) {
+ receiver.accept(new Message(buffer, result.getHandles()));
+ }
+ return result.getMojoResult();
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
new file mode 100644
index 0000000..efb9842
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -0,0 +1,630 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.bindings.Interface.Proxy;
+import org.chromium.mojo.bindings.Struct.DataHeader;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.InvalidHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.SharedBufferHandle;
+import org.chromium.mojo.system.UntypedHandle;
+
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+/**
+ * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic
+ * types from a {@link Message} object at a given offset into it's byte buffer.
+ */
+public class Decoder {
+
+ /**
+ * Helper class to validate the decoded message.
+ */
+ static final class Validator {
+
+ /**
+ * Minimal value for the next handle to deserialize.
+ */
+ private int mMinNextClaimedHandle = 0;
+ /**
+ * Minimal value of the start of the next memory to claim.
+ */
+ private long mMinNextMemory = 0;
+
+ /**
+ * The maximal memory accessible.
+ */
+ private final long mMaxMemory;
+
+ /**
+ * The number of handles in the message.
+ */
+ private final long mNumberOfHandles;
+
+ /**
+ * Constructor.
+ */
+ Validator(long maxMemory, int numberOfHandles) {
+ mMaxMemory = maxMemory;
+ mNumberOfHandles = numberOfHandles;
+ }
+
+ public void claimHandle(int handle) {
+ if (handle < mMinNextClaimedHandle) {
+ throw new DeserializationException(
+ "Trying to access handle out of order.");
+ }
+ if (handle >= mNumberOfHandles) {
+ throw new DeserializationException("Trying to access non present handle.");
+ }
+ mMinNextClaimedHandle = handle + 1;
+ }
+
+ public void claimMemory(long start, long end) {
+ if (start % BindingsHelper.ALIGNMENT != 0) {
+ throw new DeserializationException("Incorrect starting alignment: " + start + ".");
+ }
+ if (start < mMinNextMemory) {
+ throw new DeserializationException("Trying to access memory out of order.");
+ }
+ if (end < start) {
+ throw new DeserializationException("Incorrect memory range.");
+ }
+ if (end > mMaxMemory) {
+ throw new DeserializationException("Trying to access out of range memory.");
+ }
+ mMinNextMemory = BindingsHelper.align(end);
+ }
+ }
+
+ /**
+ * The message to deserialize from.
+ */
+ private final Message mMessage;
+
+ /**
+ * The base offset in the byte buffer.
+ */
+ private final int mBaseOffset;
+
+ /**
+ * Validator for the decoded message.
+ */
+ private final Validator mValidator;
+
+ /**
+ * Constructor.
+ *
+ * @param message The message to decode.
+ */
+ public Decoder(Message message) {
+ this(message, new Validator(message.getData().limit(), message.getHandles().size()), 0);
+ }
+
+ private Decoder(Message message, Validator validator, int baseOffset) {
+ mMessage = message;
+ mMessage.getData().order(ByteOrder.nativeOrder());
+ mBaseOffset = baseOffset;
+ mValidator = validator;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset.
+ */
+ public DataHeader readDataHeader() {
+ // Claim the memory for the header.
+ mValidator.claimMemory(mBaseOffset, mBaseOffset + DataHeader.HEADER_SIZE);
+ int size = readInt(DataHeader.SIZE_OFFSET);
+ int numFields = readInt(DataHeader.NUM_FIELDS_OFFSET);
+ if (size < 0) {
+ throw new DeserializationException(
+ "Negative size. Unsigned integers are not valid for java.");
+ }
+ if (numFields < 0) {
+ throw new DeserializationException(
+ "Negative number of fields. Unsigned integers are not valid for java.");
+ }
+
+ // Claim the remaining memory.
+ mValidator.claimMemory(mBaseOffset + DataHeader.HEADER_SIZE, mBaseOffset + size);
+ DataHeader res = new DataHeader(size, numFields);
+ return res;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+ * array where element have the given size.
+ */
+ public DataHeader readDataHeaderForPointerArray(int expectedLength) {
+ return readDataHeaderForArray(8, expectedLength);
+ }
+
+ /**
+ * Deserializes a byte at the given offset.
+ */
+ public byte readByte(int offset) {
+ validateBufferSize(offset, 1);
+ return mMessage.getData().get(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a boolean at the given offset, re-using any partially read byte.
+ */
+ public boolean readBoolean(int offset, int bit) {
+ validateBufferSize(offset, 1);
+ return (readByte(offset) & (1 << bit)) != 0;
+ }
+
+ /**
+ * Deserializes a short at the given offset.
+ */
+ public short readShort(int offset) {
+ validateBufferSize(offset, 2);
+ return mMessage.getData().getShort(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes an int at the given offset.
+ */
+ public int readInt(int offset) {
+ validateBufferSize(offset, 4);
+ return mMessage.getData().getInt(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a float at the given offset.
+ */
+ public float readFloat(int offset) {
+ validateBufferSize(offset, 4);
+ return mMessage.getData().getFloat(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a long at the given offset.
+ */
+ public long readLong(int offset) {
+ validateBufferSize(offset, 8);
+ return mMessage.getData().getLong(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a double at the given offset.
+ */
+ public double readDouble(int offset) {
+ validateBufferSize(offset, 8);
+ return mMessage.getData().getDouble(mBaseOffset + offset);
+ }
+
+ /**
+ * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content
+ * of the pointer.
+ */
+ public Decoder readPointer(int offset, boolean nullable) {
+ int basePosition = mBaseOffset + offset;
+ long pointerOffset = readLong(offset);
+ if (pointerOffset == 0) {
+ if (!nullable) {
+ throw new DeserializationException(
+ "Trying to decode null pointer for a non-nullable type.");
+ }
+ return null;
+ }
+ int newPosition = (int) (basePosition + pointerOffset);
+ // The method |getDecoderAtPosition| will validate that the pointer address is valid.
+ return getDecoderAtPosition(newPosition);
+
+ }
+
+ /**
+ * Deserializes an array of boolean at the given offset.
+ */
+ public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForBooleanArray(expectedLength);
+ byte[] bytes = new byte[(si.numFields + 7) / BindingsHelper.ALIGNMENT];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().get(bytes);
+ boolean[] result = new boolean[si.numFields];
+ for (int i = 0; i < bytes.length; ++i) {
+ for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
+ int booleanIndex = i * BindingsHelper.ALIGNMENT + j;
+ if (booleanIndex < result.length) {
+ result[booleanIndex] = (bytes[i] & (1 << j)) != 0;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of bytes at the given offset.
+ */
+ public byte[] readBytes(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(1, expectedLength);
+ byte[] result = new byte[si.numFields];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of shorts at the given offset.
+ */
+ public short[] readShorts(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(2, expectedLength);
+ short[] result = new short[si.numFields];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asShortBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of ints at the given offset.
+ */
+ public int[] readInts(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ int[] result = new int[si.numFields];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asIntBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of floats at the given offset.
+ */
+ public float[] readFloats(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ float[] result = new float[si.numFields];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asFloatBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of longs at the given offset.
+ */
+ public long[] readLongs(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(8, expectedLength);
+ long[] result = new long[si.numFields];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asLongBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an array of doubles at the given offset.
+ */
+ public double[] readDoubles(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(8, expectedLength);
+ double[] result = new double[si.numFields];
+ d.mMessage.getData().position(d.mBaseOffset + DataHeader.HEADER_SIZE);
+ d.mMessage.getData().asDoubleBuffer().get(result);
+ return result;
+ }
+
+ /**
+ * Deserializes an |Handle| at the given offset.
+ */
+ public Handle readHandle(int offset, boolean nullable) {
+ int index = readInt(offset);
+ if (index == -1) {
+ if (!nullable) {
+ throw new DeserializationException(
+ "Trying to decode an invalid handle for a non-nullable type.");
+ }
+ return InvalidHandle.INSTANCE;
+ }
+ mValidator.claimHandle(index);
+ return mMessage.getHandles().get(index);
+ }
+
+ /**
+ * Deserializes an |UntypedHandle| at the given offset.
+ */
+ public UntypedHandle readUntypedHandle(int offset, boolean nullable) {
+ return readHandle(offset, nullable).toUntypedHandle();
+ }
+
+ /**
+ * Deserializes a |ConsumerHandle| at the given offset.
+ */
+ public DataPipe.ConsumerHandle readConsumerHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toDataPipeConsumerHandle();
+ }
+
+ /**
+ * Deserializes a |ProducerHandle| at the given offset.
+ */
+ public DataPipe.ProducerHandle readProducerHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toDataPipeProducerHandle();
+ }
+
+ /**
+ * Deserializes a |MessagePipeHandle| at the given offset.
+ */
+ public MessagePipeHandle readMessagePipeHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toMessagePipeHandle();
+ }
+
+ /**
+ * Deserializes a |SharedBufferHandle| at the given offset.
+ */
+ public SharedBufferHandle readSharedBufferHandle(int offset, boolean nullable) {
+ return readUntypedHandle(offset, nullable).toSharedBufferHandle();
+ }
+
+ /**
+ * Deserializes an interface at the given offset.
+ *
+ * @return a proxy to the service.
+ */
+ public <P extends Proxy> P readServiceInterface(int offset, boolean nullable,
+ Interface.Manager<?, P> manager) {
+ MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+ if (!handle.isValid()) {
+ return null;
+ }
+ return manager.attachProxy(handle);
+ }
+
+ /**
+ * Deserializes a |InterfaceRequest| at the given offset.
+ */
+ public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(int offset,
+ boolean nullable) {
+ MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
+ if (handle == null) {
+ return null;
+ }
+ return new InterfaceRequest<I>(handle);
+ }
+
+ /**
+ * Deserializes a string at the given offset.
+ */
+ public String readString(int offset, boolean nullable) {
+ final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0;
+ byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
+ if (bytes == null) {
+ return null;
+ }
+ return new String(bytes, Charset.forName("utf8"));
+ }
+
+ /**
+ * Deserializes an array of |Handle| at the given offset.
+ */
+ public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ Handle[] result = new Handle[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |UntypedHandle| at the given offset.
+ */
+ public UntypedHandle[] readUntypedHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ UntypedHandle[] result = new UntypedHandle[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readUntypedHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |ConsumerHandle| at the given offset.
+ */
+ public DataPipe.ConsumerHandle[] readConsumerHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ DataPipe.ConsumerHandle[] result = new DataPipe.ConsumerHandle[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readConsumerHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |ProducerHandle| at the given offset.
+ */
+ public DataPipe.ProducerHandle[] readProducerHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ DataPipe.ProducerHandle[] result = new DataPipe.ProducerHandle[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readProducerHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+
+ }
+
+ /**
+ * Deserializes an array of |MessagePipeHandle| at the given offset.
+ */
+ public MessagePipeHandle[] readMessagePipeHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ MessagePipeHandle[] result = new MessagePipeHandle[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readMessagePipeHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+
+ }
+
+ /**
+ * Deserializes an array of |SharedBufferHandle| at the given offset.
+ */
+ public SharedBufferHandle[] readSharedBufferHandles(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ SharedBufferHandle[] result = new SharedBufferHandle[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readSharedBufferHandle(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+
+ }
+
+ /**
+ * Deserializes an array of |ServiceHandle| at the given offset.
+ */
+ public <S extends Interface, P extends Proxy> S[] readServiceInterfaces(
+ int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ S[] result = manager.buildArray(si.numFields);
+ for (int i = 0; i < result.length; ++i) {
+ // This cast is necessary because java 6 doesn't handle wildcard correctly when using
+ // Manager<S, ? extends S>
+ @SuppressWarnings("unchecked")
+ S value = (S) d.readServiceInterface(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability), manager);
+ result[i] = value;
+ }
+ return result;
+ }
+
+ /**
+ * Deserializes an array of |InterfaceRequest| at the given offset.
+ */
+ public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests(
+ int offset, int arrayNullability, int expectedLength) {
+ Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ if (d == null) {
+ return null;
+ }
+ DataHeader si = d.readDataHeaderForArray(4, expectedLength);
+ @SuppressWarnings("unchecked")
+ InterfaceRequest<I>[] result = new InterfaceRequest[si.numFields];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = d.readInterfaceRequest(
+ DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ return result;
+ }
+
+ /**
+ * Returns a view of this decoder at the offset |offset|.
+ */
+ private Decoder getDecoderAtPosition(int offset) {
+ return new Decoder(mMessage, mValidator, offset);
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} at the given offset and checks if it is correct for an
+ * array of booleans.
+ */
+ private DataHeader readDataHeaderForBooleanArray(int expectedLength) {
+ DataHeader dataHeader = readDataHeader();
+ if (dataHeader.size < DataHeader.HEADER_SIZE + (dataHeader.numFields + 7) / 8) {
+ throw new DeserializationException("Array header is incorrect.");
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && dataHeader.numFields != expectedLength) {
+ throw new DeserializationException("Incorrect array length. Expected: " +
+ expectedLength + ", but got: " + dataHeader.numFields + ".");
+ }
+ return dataHeader;
+ }
+
+ /**
+ * Deserializes a {@link DataHeader} of an array at the given offset.
+ */
+ private DataHeader readDataHeaderForArray(long elementSize, int expectedLength) {
+ DataHeader dataHeader = readDataHeader();
+ if (dataHeader.size < (DataHeader.HEADER_SIZE + elementSize * dataHeader.numFields)) {
+ throw new DeserializationException("Array header is incorrect.");
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+ && dataHeader.numFields != expectedLength) {
+ throw new DeserializationException("Incorrect array length. Expected: " +
+ expectedLength + ", but got: " + dataHeader.numFields + ".");
+ }
+ return dataHeader;
+ }
+
+ private void validateBufferSize(int offset, int size) {
+ if (mMessage.getData().limit() < offset + size) {
+ throw new DeserializationException("Buffer is smaller than expected.");
+ }
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
new file mode 100644
index 0000000..2ee2ae7
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MojoException;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * A {@link ConnectionErrorHandler} that delegate the errors to a list of registered handlers. This
+ * class will use weak pointers to prevent keeping references to any handlers it delegates to.
+ */
+public class DelegatingConnectionErrorHandler implements ConnectionErrorHandler {
+
+ /**
+ * The registered handlers. This uses a {@link WeakHashMap} so that it doesn't prevent the
+ * handler from being garbage collected.
+ */
+ private final Set<ConnectionErrorHandler> mHandlers =
+ Collections.newSetFromMap(new WeakHashMap<ConnectionErrorHandler, Boolean>());
+
+ /**
+ * @see ConnectionErrorHandler#onConnectionError(MojoException)
+ */
+ @Override
+ public void onConnectionError(MojoException e) {
+ for (ConnectionErrorHandler handler : mHandlers) {
+ handler.onConnectionError(e);
+ }
+ }
+
+ /**
+ * Add a handler that will be notified of any error this object receives.
+ */
+ public void addConnectionErrorHandler(ConnectionErrorHandler handler) {
+ mHandlers.add(handler);
+ }
+
+ /**
+ * Remove a previously registered handler.
+ */
+ public void removeConnectionErrorHandler(ConnectionErrorHandler handler) {
+ mHandlers.remove(handler);
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
new file mode 100644
index 0000000..eeb511c
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Error when deserializing a mojo message.
+ */
+public class DeserializationException extends RuntimeException {
+
+ /**
+ * Constructs a new deserialization exception with the specified detail message.
+ */
+ public DeserializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new deserialization exception with the specified cause.
+ */
+ public DeserializationException(Exception cause) {
+ super(cause);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
new file mode 100644
index 0000000..e3b2bfe
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
@@ -0,0 +1,518 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.bindings.Struct.DataHeader;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Pair;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed.
+ * It also keeps track of the associated handles, and the offset of the current data section.
+ */
+public class Encoder {
+
+ /**
+ * Container class for all state that must be shared between the main encoder and any used sub
+ * encoder.
+ */
+ private static class EncoderState {
+
+ /**
+ * The core used to encode interfaces.
+ */
+ public final Core core;
+
+ /**
+ * The ByteBuffer to which the message will be encoded.
+ */
+ public ByteBuffer byteBuffer;
+
+ /**
+ * The list of encountered handles.
+ */
+ public final List<Handle> handles = new ArrayList<Handle>();
+
+ /**
+ * The current absolute position for the next data section.
+ */
+ public int dataEnd;
+
+ /**
+ * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+ * being encoded contains interfaces, can be |null| otherwise.
+ * @param bufferSize A hint on the size of the message. Used to build the initial byte
+ * buffer.
+ */
+ private EncoderState(Core core, int bufferSize) {
+ assert bufferSize % BindingsHelper.ALIGNMENT == 0;
+ this.core = core;
+ byteBuffer = ByteBuffer.allocateDirect(
+ bufferSize > 0 ? bufferSize : INITIAL_BUFFER_SIZE);
+ byteBuffer.order(ByteOrder.nativeOrder());
+ dataEnd = 0;
+ }
+
+ /**
+ * Claim the given amount of memory at the end of the buffer, resizing it if needed.
+ */
+ public void claimMemory(int size) {
+ dataEnd += size;
+ growIfNeeded();
+ }
+
+ /**
+ * Grow the associated ByteBuffer if needed.
+ */
+ private void growIfNeeded() {
+ if (byteBuffer.capacity() >= dataEnd) {
+ return;
+ }
+ int targetSize = byteBuffer.capacity() * 2;
+ while (targetSize < dataEnd) {
+ targetSize *= 2;
+ }
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(targetSize);
+ newBuffer.order(ByteOrder.nativeOrder());
+ byteBuffer.position(0);
+ byteBuffer.limit(byteBuffer.capacity());
+ newBuffer.put(byteBuffer);
+ byteBuffer = newBuffer;
+ }
+ }
+
+ /**
+ * Default initial size of the data buffer. This must be a multiple of 8 bytes.
+ */
+ private static final int INITIAL_BUFFER_SIZE = 1024;
+
+ /**
+ * Base offset in the byte buffer for writing.
+ */
+ private int mBaseOffset;
+
+ /**
+ * The encoder state shared by the main encoder and all its sub-encoder.
+ */
+ private final EncoderState mEncoderState;
+
+ /**
+ * Returns the result message.
+ */
+ public Message getMessage() {
+ mEncoderState.byteBuffer.position(0);
+ mEncoderState.byteBuffer.limit(mEncoderState.dataEnd);
+ return new Message(mEncoderState.byteBuffer, mEncoderState.handles);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+ * being encoded contains interfaces, can be |null| otherwise.
+ * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer.
+ */
+ public Encoder(Core core, int sizeHint) {
+ this(new EncoderState(core, sizeHint));
+ }
+
+ /**
+ * Private constructor for sub-encoders.
+ */
+ private Encoder(EncoderState bufferInformation) {
+ mEncoderState = bufferInformation;
+ mBaseOffset = bufferInformation.dataEnd;
+ }
+
+ /**
+ * Returns a new encoder that will append to the current buffer.
+ */
+ public Encoder getEncoderAtDataOffset(DataHeader dataHeader) {
+ Encoder result = new Encoder(mEncoderState);
+ result.encode(dataHeader);
+ return result;
+ }
+
+ /**
+ * Encode a {@link DataHeader} and claim the amount of memory required for the data section
+ * (resizing the buffer if required).
+ */
+ public void encode(DataHeader s) {
+ mEncoderState.claimMemory(BindingsHelper.align(s.size));
+ encode(s.size, DataHeader.SIZE_OFFSET);
+ encode(s.numFields, DataHeader.NUM_FIELDS_OFFSET);
+ }
+
+ /**
+ * Encode a byte at the given offset.
+ */
+ public void encode(byte v, int offset) {
+ mEncoderState.byteBuffer.put(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a boolean at the given offset.
+ */
+ public void encode(boolean v, int offset, int bit) {
+ if (v) {
+ byte encodedValue = mEncoderState.byteBuffer.get(mBaseOffset + offset);
+ encodedValue |= 1 << bit;
+ mEncoderState.byteBuffer.put(mBaseOffset + offset, encodedValue);
+ }
+ }
+
+ /**
+ * Encode a short at the given offset.
+ */
+ public void encode(short v, int offset) {
+ mEncoderState.byteBuffer.putShort(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode an int at the given offset.
+ */
+ public void encode(int v, int offset) {
+ mEncoderState.byteBuffer.putInt(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a float at the given offset.
+ */
+ public void encode(float v, int offset) {
+ mEncoderState.byteBuffer.putFloat(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a long at the given offset.
+ */
+ public void encode(long v, int offset) {
+ mEncoderState.byteBuffer.putLong(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a double at the given offset.
+ */
+ public void encode(double v, int offset) {
+ mEncoderState.byteBuffer.putDouble(mBaseOffset + offset, v);
+ }
+
+ /**
+ * Encode a {@link Struct} at the given offset.
+ */
+ public void encode(Struct v, int offset, boolean nullable) {
+ if (v == null) {
+ encodeNullPointer(offset, nullable);
+ return;
+ }
+ encodePointerToNextUnclaimedData(offset);
+ v.encode(this);
+ }
+
+ /**
+ * Encodes a String.
+ */
+ public void encode(String v, int offset, boolean nullable) {
+ if (v == null) {
+ encodeNullPointer(offset, nullable);
+ return;
+ }
+ final int arrayNullability = nullable ?
+ BindingsHelper.ARRAY_NULLABLE : BindingsHelper.NOTHING_NULLABLE;
+ encode(v.getBytes(Charset.forName("utf8")), offset, arrayNullability,
+ BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
+ }
+
+ /**
+ * Encodes a {@link Handle}.
+ */
+ public void encode(Handle v, int offset, boolean nullable) {
+ if (v == null || !v.isValid()) {
+ encodeInvalidHandle(offset, nullable);
+ } else {
+ encode(mEncoderState.handles.size(), offset);
+ mEncoderState.handles.add(v);
+ }
+ }
+
+ /**
+ * Encode an {@link Interface}.
+ */
+ public <T extends Interface> void encode(T v, int offset, boolean nullable,
+ Interface.Manager<T, ?> manager) {
+ if (v == null) {
+ encodeInvalidHandle(offset, nullable);
+ return;
+ }
+ if (mEncoderState.core == null) {
+ throw new UnsupportedOperationException(
+ "The encoder has been created without a Core. It can't encode an interface.");
+ }
+ // If the instance is a proxy, pass the proxy's handle instead of creating a new stub.
+ if (v instanceof Interface.AbstractProxy) {
+ Interface.AbstractProxy proxy = (Interface.AbstractProxy) v;
+ if (proxy.getMessageReceiver() instanceof HandleOwner) {
+ encode(((HandleOwner<?>) proxy.getMessageReceiver()).passHandle(), offset,
+ nullable);
+ return;
+ }
+ // If the proxy is not over a message pipe, the default case applies.
+ }
+ Pair<MessagePipeHandle, MessagePipeHandle> handles =
+ mEncoderState.core.createMessagePipe(null);
+ manager.bind(v, handles.first);
+ encode(handles.second, offset, nullable);
+ }
+
+ /**
+ * Encode an {@link InterfaceRequest}.
+ */
+ public <I extends Interface> void encode(InterfaceRequest<I> v, int offset, boolean nullable) {
+ if (v == null) {
+ encodeInvalidHandle(offset, nullable);
+ return;
+ }
+ if (mEncoderState.core == null) {
+ throw new UnsupportedOperationException(
+ "The encoder has been created without a Core. It can't encode an interface.");
+ }
+ encode(v.passHandle(), offset, nullable);
+ }
+
+ /**
+ * Returns an {@link Encoder} suitable for encoding an array of pointer of the given length.
+ */
+ public Encoder encodePointerArray(int length, int offset, int expectedLength) {
+ return encoderForArray(BindingsHelper.POINTER_SIZE, length, offset, expectedLength);
+ }
+
+ /**
+ * Encodes an array of booleans.
+ */
+ public void encode(boolean[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
+ expectedLength != v.length) {
+ throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+ }
+ byte[] bytes = new byte[(v.length + 7) / BindingsHelper.ALIGNMENT];
+ for (int i = 0; i < bytes.length; ++i) {
+ for (int j = 0; j < BindingsHelper.ALIGNMENT; ++j) {
+ int booleanIndex = BindingsHelper.ALIGNMENT * i + j;
+ if (booleanIndex < v.length && v[booleanIndex]) {
+ bytes[i] |= (1 << j);
+ }
+ }
+ }
+ encodeByteArray(bytes, v.length, offset);
+ }
+
+ /**
+ * Encodes an array of bytes.
+ */
+ public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
+ expectedLength != v.length) {
+ throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+ }
+ encodeByteArray(v, v.length, offset);
+ }
+
+ /**
+ * Encodes an array of shorts.
+ */
+ public void encode(short[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(2, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of ints.
+ */
+ public void encode(int[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(4, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of floats.
+ */
+ public void encode(float[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(4, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of longs.
+ */
+ public void encode(long[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(8, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of doubles.
+ */
+ public void encode(double[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ encoderForArray(8, v.length, offset, expectedLength).append(v);
+ }
+
+ /**
+ * Encodes an array of {@link Handle}.
+ */
+ public void encode(Handle[] v, int offset, int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ Encoder e = encoderForArray(
+ BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+ for (int i = 0; i < v.length; ++i) {
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ }
+
+ /**
+ * Encodes an array of {@link Interface}.
+ */
+ public <T extends Interface> void encode(T[] v, int offset, int arrayNullability,
+ int expectedLength, Interface.Manager<T, ?> manager) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ Encoder e = encoderForArray(
+ BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+ for (int i = 0; i < v.length; ++i) {
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability), manager);
+ }
+ }
+
+ /**
+ * Encodes an array of {@link InterfaceRequest}.
+ */
+ public <I extends Interface> void encode(InterfaceRequest<I>[] v, int offset,
+ int arrayNullability, int expectedLength) {
+ if (v == null) {
+ encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
+ return;
+ }
+ Encoder e = encoderForArray(
+ BindingsHelper.SERIALIZED_HANDLE_SIZE, v.length, offset, expectedLength);
+ for (int i = 0; i < v.length; ++i) {
+ e.encode(v[i], DataHeader.HEADER_SIZE + BindingsHelper.SERIALIZED_HANDLE_SIZE * i,
+ BindingsHelper.isElementNullable(arrayNullability));
+ }
+ }
+
+ /**
+ * Encodes a <code>null</code> pointer iff the object is nullable, raises an exception
+ * otherwise.
+ */
+ public void encodeNullPointer(int offset, boolean nullable) {
+ if (!nullable) {
+ throw new SerializationException(
+ "Trying to encode a null pointer for a non-nullable type.");
+ }
+ mEncoderState.byteBuffer.putLong(mBaseOffset + offset, 0);
+ }
+
+ /**
+ * Encodes an invalid handle iff the object is nullable, raises an exception otherwise.
+ */
+ public void encodeInvalidHandle(int offset, boolean nullable) {
+ if (!nullable) {
+ throw new SerializationException(
+ "Trying to encode an invalid handle for a non-nullable type.");
+ }
+ mEncoderState.byteBuffer.putInt(mBaseOffset + offset, -1);
+ }
+
+ private void encodePointerToNextUnclaimedData(int offset) {
+ encode((long) mEncoderState.dataEnd - (mBaseOffset + offset), offset);
+ }
+
+ private Encoder encoderForArray(
+ int elementSizeInByte, int length, int offset, int expectedLength) {
+ if (expectedLength != BindingsHelper.UNSPECIFIED_ARRAY_LENGTH &&
+ expectedLength != length) {
+ throw new SerializationException("Trying to encode a fixed array of incorrect length.");
+ }
+ return encoderForArrayByTotalSize(length * elementSizeInByte, length, offset);
+ }
+
+ private Encoder encoderForArrayByTotalSize(int byteSize, int length, int offset) {
+ encodePointerToNextUnclaimedData(offset);
+ return getEncoderAtDataOffset(
+ new DataHeader(DataHeader.HEADER_SIZE + byteSize, length));
+ }
+
+ private void encodeByteArray(byte[] bytes, int length, int offset) {
+ encoderForArrayByTotalSize(bytes.length, length, offset).append(bytes);
+ }
+
+ private void append(byte[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.put(v);
+ }
+
+ private void append(short[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asShortBuffer().put(v);
+ }
+
+ private void append(int[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asIntBuffer().put(v);
+ }
+
+ private void append(float[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asFloatBuffer().put(v);
+ }
+
+ private void append(double[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asDoubleBuffer().put(v);
+ }
+
+ private void append(long[] v) {
+ mEncoderState.byteBuffer.position(mBaseOffset + DataHeader.HEADER_SIZE);
+ mEncoderState.byteBuffer.asLongBuffer().put(v);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
new file mode 100644
index 0000000..d0d5c3b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -0,0 +1,184 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.AsyncWaiter.Callback;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A factory which provides per-thread executors, which enable execution on the thread from which
+ * they were obtained.
+ */
+class ExecutorFactory {
+
+ /**
+ * A null buffer which is used to send messages without any data on the PipedExecutor's
+ * signaling handles.
+ */
+ private static final ByteBuffer NOTIFY_BUFFER = null;
+
+ /**
+ * Implementation of the executor which uses a pair of {@link MessagePipeHandle} for signaling.
+ * The executor will wait asynchronously on one end of a {@link MessagePipeHandle} on the thread
+ * on which it was created. Other threads can call execute with a {@link Runnable}, and the
+ * executor will queue the {@link Runnable} and write a message on the other end of the handle.
+ * This will wake up the executor which is waiting on the handle, which will then dequeue the
+ * {@link Runnable} and execute it on the original thread.
+ */
+ private static class PipedExecutor implements Executor, Callback {
+
+ /**
+ * The handle which is written to. Access to this object must be protected with |mLock|.
+ */
+ private final MessagePipeHandle mWriteHandle;
+ /**
+ * The handle which is read from.
+ */
+ private final MessagePipeHandle mReadHandle;
+ /**
+ * The list of actions left to be run. Access to this object must be protected with |mLock|.
+ */
+ private final List<Runnable> mPendingActions;
+ /**
+ * Lock protecting access to |mWriteHandle| and |mPendingActions|.
+ */
+ private final Object mLock;
+ /**
+ * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|.
+ */
+ private final AsyncWaiter mWaiter;
+
+ /**
+ * Constructor.
+ */
+ public PipedExecutor(Core core) {
+ mWaiter = core.getDefaultAsyncWaiter();
+ assert mWaiter != null;
+ mLock = new Object();
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(
+ new MessagePipeHandle.CreateOptions());
+ mReadHandle = handles.first;
+ mWriteHandle = handles.second;
+ mPendingActions = new ArrayList<Runnable>();
+ asyncWait();
+ }
+
+ /**
+ * Asynchronously wait for the next command to arrive. This should only be called on the
+ * executor thread.
+ */
+ private void asyncWait() {
+ mWaiter.asyncWait(mReadHandle, Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE,
+ this);
+ }
+
+ /**
+ * @see Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ if (result == MojoResult.OK && readNotifyBufferMessage()) {
+ runNextAction();
+ } else {
+ close();
+ }
+ }
+
+ /**
+ * @see Callback#onError(MojoException)
+ */
+ @Override
+ public void onError(MojoException exception) {
+ close();
+ }
+
+ /**
+ * Close the handles. Should only be called on the executor thread.
+ */
+ private void close() {
+ synchronized (mLock) {
+ mWriteHandle.close();
+ mPendingActions.clear();
+ }
+ mReadHandle.close();
+ }
+
+ /**
+ * Read the next message on |mReadHandle|, and return |true| if successful, |false|
+ * otherwise.
+ */
+ private boolean readNotifyBufferMessage() {
+ try {
+ ReadMessageResult readMessageResult = mReadHandle.readMessage(NOTIFY_BUFFER, 0,
+ MessagePipeHandle.ReadFlags.NONE);
+ if (readMessageResult.getMojoResult() == MojoResult.OK) {
+ asyncWait();
+ return true;
+ }
+ } catch (MojoException e) {
+ // Will be closed by the fall back at the end of this method.
+ }
+ return false;
+ }
+
+ /**
+ * Run the next action in the |mPendingActions| queue.
+ */
+ private void runNextAction() {
+ Runnable toRun = null;
+ synchronized (mWriteHandle) {
+ toRun = mPendingActions.remove(0);
+ }
+ toRun.run();
+ }
+
+ /**
+ * Execute the given |command| in the executor thread. This can be called on any thread.
+ *
+ * @see Executor#execute(Runnable)
+ */
+ @Override
+ public void execute(Runnable command) {
+ // Accessing the write handle must be protected by the lock, because it can be closed
+ // from the executor's thread.
+ synchronized (mLock) {
+ if (!mWriteHandle.isValid()) {
+ throw new IllegalStateException(
+ "Trying to execute an action on a closed executor.");
+ }
+ mPendingActions.add(command);
+ mWriteHandle.writeMessage(NOTIFY_BUFFER, null, MessagePipeHandle.WriteFlags.NONE);
+ }
+ }
+ }
+
+ /**
+ * Keep one executor per executor thread.
+ */
+ private static final ThreadLocal<Executor> EXECUTORS = new ThreadLocal<Executor>();
+
+ /**
+ * Returns an {@link Executor} that will run all of its actions in the current thread.
+ */
+ public static Executor getExecutorForCurrentThread(Core core) {
+ Executor executor = EXECUTORS.get();
+ if (executor == null) {
+ executor = new PipedExecutor(core);
+ EXECUTORS.set(executor);
+ }
+ return executor;
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
new file mode 100644
index 0000000..60fc33b
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Handle;
+
+import java.io.Closeable;
+
+/**
+ * Describes a class that owns a handle.
+ *
+ * @param <H> The type of the owned handle.
+ */
+public interface HandleOwner<H extends Handle> extends Closeable {
+
+ /**
+ * Pass the handle owned by this class.
+ */
+ public H passHandle();
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close();
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
new file mode 100644
index 0000000..c3b26a2
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -0,0 +1,257 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.Pair;
+
+import java.io.Closeable;
+
+/**
+ * Base class for mojo generated interfaces.
+ */
+public interface Interface extends ConnectionErrorHandler, Closeable {
+
+ /**
+ * The close method is called when the connection to the interface is closed.
+ *
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close();
+
+ /**
+ * A proxy to a mojo interface. This is base class for all generated proxies. It implements the
+ * Interface and each time a method is called, the parameters are serialized and sent to the
+ * {@link MessageReceiverWithResponder}, along with the response callback if needed.
+ */
+ public interface Proxy extends Interface {
+
+ /**
+ * Set the {@link ConnectionErrorHandler} that will be notified of errors.
+ */
+ public void setErrorHandler(ConnectionErrorHandler errorHandler);
+
+ }
+
+ /**
+ * Base implementation of {@link Proxy}.
+ */
+ abstract class AbstractProxy implements Proxy {
+
+ /**
+ * The {@link Core} implementation to use.
+ */
+ private final Core mCore;
+
+ /**
+ * The {@link MessageReceiverWithResponder} that will receive a serialized message for each
+ * method call.
+ */
+ private final MessageReceiverWithResponder mMessageReceiver;
+
+ /**
+ * The {@link ConnectionErrorHandler} that will be notified of errors.
+ */
+ private ConnectionErrorHandler mErrorHandler = null;
+
+ /**
+ * Constructor.
+ *
+ * @param core the Core implementation used to create pipes and access the async waiter.
+ * @param messageReceiver the message receiver to send message to.
+ */
+ protected AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) {
+ this.mCore = core;
+ this.mMessageReceiver = messageReceiver;
+ }
+
+ /**
+ * Returns the message receiver to send message to.
+ */
+ protected MessageReceiverWithResponder getMessageReceiver() {
+ return mMessageReceiver;
+ }
+
+ /**
+ * Returns the Core implementation.
+ */
+ protected Core getCore() {
+ return mCore;
+ }
+
+ /**
+ * @see Proxy#setErrorHandler(ConnectionErrorHandler)
+ */
+ @Override
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ this.mErrorHandler = errorHandler;
+ }
+
+ /**
+ * @see ConnectionErrorHandler#onConnectionError(MojoException)
+ */
+ @Override
+ public void onConnectionError(MojoException e) {
+ if (mErrorHandler != null) {
+ mErrorHandler.onConnectionError(e);
+ }
+ }
+
+ /**
+ * @see Closeable#close()
+ */
+ @Override
+ public void close() {
+ mMessageReceiver.close();
+ }
+ }
+
+ /**
+ * Base implementation of Stub. Stubs are message receivers that deserialize the payload and
+ * call the appropriate method in the implementation. If the method returns result, the stub
+ * serializes the response and sends it back.
+ *
+ * @param <I> the type of the interface to delegate calls to.
+ */
+ abstract class Stub<I extends Interface> implements MessageReceiverWithResponder {
+
+ /**
+ * The {@link Core} implementation to use.
+ */
+ private final Core mCore;
+
+ /**
+ * The implementation to delegate calls to.
+ */
+ private final I mImpl;
+
+ /**
+ * Constructor.
+ *
+ * @param core the {@link Core} implementation to use.
+ * @param impl the implementation to delegate calls to.
+ */
+ public Stub(Core core, I impl) {
+ mCore = core;
+ mImpl = impl;
+ }
+
+ /**
+ * Returns the Core implementation.
+ */
+ protected Core getCore() {
+ return mCore;
+ }
+
+ /**
+ * Returns the implementation to delegate calls to.
+ */
+ protected I getImpl() {
+ return mImpl;
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.MessageReceiver#close()
+ */
+ @Override
+ public void close() {
+ mImpl.close();
+ }
+
+ }
+
+ /**
+ * The |Manager| object enables building of proxies and stubs for a given interface.
+ *
+ * @param <I> the type of the interface the manager can handle.
+ * @param <P> the type of the proxy the manager can handle. To be noted, P always extends I.
+ */
+ abstract class Manager<I extends Interface, P extends Proxy> {
+
+ /**
+ * Returns the name of the interface. This is an opaque (but human readable) identifier used
+ * by the service provider to identify services.
+ */
+ public abstract String getName();
+
+ /**
+ * Binds the given implementation to the handle.
+ */
+ public void bind(I impl, MessagePipeHandle handle) {
+ // The router (and by consequence the handle) is intentionally leaked. It will close
+ // itself when the connected handle is closed and the proxy receives the connection
+ // error.
+ Router router = new RouterImpl(handle);
+ bind(handle.getCore(), impl, router);
+ router.start();
+ }
+
+ /**
+ * Binds the given implementation to the InterfaceRequest.
+ */
+ public final void bind(I impl, InterfaceRequest<I> request) {
+ bind(impl, request.passHandle());
+ }
+
+ /**
+ * Returns a Proxy that will send messages to the given |handle|. This implies that the
+ * other end of the handle must be binded to an implementation of the interface.
+ */
+ public final P attachProxy(MessagePipeHandle handle) {
+ RouterImpl router = new RouterImpl(handle);
+ P proxy = attachProxy(handle.getCore(), router);
+ DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler();
+ handlers.addConnectionErrorHandler(proxy);
+ router.setErrorHandler(handlers);
+ router.start();
+ return proxy;
+ }
+
+ /**
+ * Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where
+ * the first element is a proxy, and the second element is the request. The proxy can be
+ * used immediately.
+ */
+ public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core) {
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ P proxy = attachProxy(handles.first);
+ return Pair.create(proxy, new InterfaceRequest<I>(handles.second));
+ }
+
+ /**
+ * Binds the implementation to the given |router|.
+ */
+ final void bind(Core core, I impl, Router router) {
+ router.setErrorHandler(impl);
+ router.setIncomingMessageReceiver(buildStub(core, impl));
+ }
+
+ /**
+ * Returns a Proxy that will send messages to the given |router|.
+ */
+ final P attachProxy(Core core, Router router) {
+ return buildProxy(core, new AutoCloseableRouter(core, router));
+ }
+
+ /**
+ * Creates a new array of the given |size|.
+ */
+ protected abstract I[] buildArray(int size);
+
+ /**
+ * Constructs a Stub delegating to the given implementation.
+ */
+ protected abstract Stub<I> buildStub(Core core, I impl);
+
+ /**
+ * Constructs a Proxy forwarding the calls to the given message receiver.
+ */
+ protected abstract P buildProxy(Core core, MessageReceiverWithResponder messageReceiver);
+
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
new file mode 100644
index 0000000..87835e0
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MessagePipeHandle;
+
+/**
+ * One end of the message pipe representing a request to create an implementation to be bound to it.
+ * The other end of the pipe is bound to a proxy, which can be used immediately, while the
+ * InterfaceRequest is being sent.
+ * <p>
+ * InterfaceRequest are built using |Interface.Manager|.
+ *
+ * @param <P> the type of the remote interface proxy.
+ */
+public class InterfaceRequest<P extends Interface> implements HandleOwner<MessagePipeHandle> {
+
+ /**
+ * The handle which will be sent and will be connected to the implementation.
+ */
+ private final MessagePipeHandle mHandle;
+
+ /**
+ * Constructor.
+ *
+ * @param handle the handle which will be sent and will be connected to the implementation.
+ */
+ InterfaceRequest(MessagePipeHandle handle) {
+ mHandle = handle;
+ }
+
+ /**
+ * @see HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ return mHandle.pass();
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ mHandle.close();
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java
new file mode 100644
index 0000000..f7d8afe
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceWithClient.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Pair;
+
+/**
+ * Base class for mojo generated interfaces that have a client.
+ *
+ * @param <CI> the type of the client interface.
+ */
+public interface InterfaceWithClient<CI extends Interface> extends Interface {
+
+ /**
+ * Proxy class for interfaces with a client.
+ */
+ interface Proxy<CI extends Interface> extends Interface.Proxy, InterfaceWithClient<CI> {
+ }
+
+ /**
+ * Base implementation of Proxy.
+ *
+ * @param <CI> the type of the client interface.
+ */
+ abstract class AbstractProxy<CI extends Interface> extends Interface.AbstractProxy
+ implements Proxy<CI> {
+
+ /**
+ * Constructor.
+ *
+ * @param core the Core implementation used to create pipes and access the async waiter.
+ * @param messageReceiver the message receiver to send message to.
+ */
+ public AbstractProxy(Core core, MessageReceiverWithResponder messageReceiver) {
+ super(core, messageReceiver);
+ }
+
+ /**
+ * @see InterfaceWithClient#setClient(Interface)
+ */
+ @Override
+ public void setClient(CI client) {
+ throw new UnsupportedOperationException(
+ "Setting the client on a proxy is not supported");
+ }
+ }
+
+ /**
+ * Base manager implementation for interfaces that have a client.
+ *
+ * @param <I> the type of the interface the manager can handle.
+ * @param <P> the type of the proxy the manager can handle. To be noted, P always extends I.
+ * @param <CI> the type of the client interface.
+ */
+ abstract class Manager<I extends InterfaceWithClient<CI>, P extends Proxy<CI>,
+ CI extends Interface> extends Interface.Manager<I, P> {
+
+ /**
+ * @see Interface.Manager#bind(Interface, MessagePipeHandle)
+ */
+ @Override
+ public final void bind(I impl, MessagePipeHandle handle) {
+ Router router = new RouterImpl(handle);
+ super.bind(handle.getCore(), impl, router);
+ @SuppressWarnings("unchecked")
+ CI client = (CI) getClientManager().attachProxy(handle.getCore(), router);
+ impl.setClient(client);
+ router.start();
+ }
+
+ /**
+ * Returns a Proxy that will send messages to the given |handle|. This implies that the
+ * other end of the handle must be connected to an implementation of the interface. |client|
+ * is the implementation of the client interface.
+ */
+ public P attachProxy(MessagePipeHandle handle, CI client) {
+ Router router = new RouterImpl(handle);
+ DelegatingConnectionErrorHandler handlers = new DelegatingConnectionErrorHandler();
+ handlers.addConnectionErrorHandler(client);
+ router.setErrorHandler(handlers);
+ getClientManager().bind(handle.getCore(), client, router);
+ P proxy = super.attachProxy(handle.getCore(), router);
+ handlers.addConnectionErrorHandler(proxy);
+ router.start();
+ return proxy;
+ }
+
+ /**
+ * Constructs a new |InterfaceRequest| for the interface. This method returns a Pair where
+ * the first element is a proxy, and the second element is the request. The proxy can be
+ * used immediately.
+ *
+ * @param client the implementation of the client interface.
+ */
+ public final Pair<P, InterfaceRequest<I>> getInterfaceRequest(Core core, CI client) {
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ P proxy = attachProxy(handles.first, client);
+ return Pair.create(proxy, new InterfaceRequest<I>(handles.second));
+ }
+
+ /**
+ * Returns a manager for the client inetrafce.
+ */
+ protected abstract Interface.Manager<CI, ?> getClientManager();
+ }
+
+ /**
+ * Set the client implementation for this interface.
+ */
+ public void setClient(CI client);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
new file mode 100644
index 0000000..0d270cc
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
@@ -0,0 +1,69 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A raw message to be sent/received from a {@link MessagePipeHandle}. Note that this can contain
+ * any data, not necessarily a Mojo message with a proper header. See also {@link ServiceMessage}.
+ */
+public class Message {
+
+ /**
+ * The data of the message.
+ */
+ private final ByteBuffer mBuffer;
+
+ /**
+ * The handles of the message.
+ */
+ private final List<? extends Handle> mHandle;
+
+ /**
+ * This message interpreted as a message for a mojo service with an appropriate header.
+ */
+ private ServiceMessage mWithHeader = null;
+
+ /**
+ * Constructor.
+ *
+ * @param buffer The buffer containing the bytes to send. This must be a direct buffer.
+ * @param handles The list of handles to send.
+ */
+ public Message(ByteBuffer buffer, List<? extends Handle> handles) {
+ assert buffer.isDirect();
+ mBuffer = buffer;
+ mHandle = handles;
+ }
+
+ /**
+ * The data of the message.
+ */
+ public ByteBuffer getData() {
+ return mBuffer;
+ }
+
+ /**
+ * The handles of the message.
+ */
+ public List<? extends Handle> getHandles() {
+ return mHandle;
+ }
+
+ /**
+ * Returns the message interpreted as a message for a mojo service.
+ */
+ public ServiceMessage asServiceMessage() {
+ if (mWithHeader == null) {
+ mWithHeader = new ServiceMessage(this);
+ }
+ return mWithHeader;
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
new file mode 100644
index 0000000..ad4e108
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
@@ -0,0 +1,254 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.bindings.Struct.DataHeader;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Header information for a message.
+ */
+public class MessageHeader {
+
+ private static final int SIMPLE_MESSAGE_SIZE = 16;
+ private static final int SIMPLE_MESSAGE_NUM_FIELDS = 2;
+ private static final DataHeader SIMPLE_MESSAGE_STRUCT_INFO =
+ new DataHeader(SIMPLE_MESSAGE_SIZE, SIMPLE_MESSAGE_NUM_FIELDS);
+
+ private static final int MESSAGE_WITH_REQUEST_ID_SIZE = 24;
+ private static final int MESSAGE_WITH_REQUEST_ID_NUM_FIELDS = 3;
+ private static final DataHeader MESSAGE_WITH_REQUEST_ID_STRUCT_INFO =
+ new DataHeader(MESSAGE_WITH_REQUEST_ID_SIZE, MESSAGE_WITH_REQUEST_ID_NUM_FIELDS);
+
+ private static final int TYPE_OFFSET = 8;
+ private static final int FLAGS_OFFSET = 12;
+ private static final int REQUEST_ID_OFFSET = 16;
+
+ /**
+ * Flag for a header of a simple message.
+ */
+ public static final int NO_FLAG = 0;
+
+ /**
+ * Flag for a header of a message that expected a response.
+ */
+ public static final int MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0;
+
+ /**
+ * Flag for a header of a message that is a response.
+ */
+ public static final int MESSAGE_IS_RESPONSE_FLAG = 1 << 1;
+
+ private final DataHeader mDataHeader;
+ private final int mType;
+ private final int mFlags;
+ private long mRequestId;
+
+ /**
+ * Constructor for the header of a message which does not have a response.
+ */
+ public MessageHeader(int type) {
+ mDataHeader = SIMPLE_MESSAGE_STRUCT_INFO;
+ mType = type;
+ mFlags = 0;
+ mRequestId = 0;
+ }
+
+ /**
+ * Constructor for the header of a message which have a response or being itself a response.
+ */
+ public MessageHeader(int type, int flags, long requestId) {
+ assert mustHaveRequestId(flags);
+ mDataHeader = MESSAGE_WITH_REQUEST_ID_STRUCT_INFO;
+ mType = type;
+ mFlags = flags;
+ mRequestId = requestId;
+ }
+
+ /**
+ * Constructor, parsing the header from a message. Should only be used by {@link Message}
+ * itself.
+ */
+ MessageHeader(Message message) {
+ Decoder decoder = new Decoder(message);
+ mDataHeader = decoder.readDataHeader();
+ validateDataHeader(mDataHeader);
+ mType = decoder.readInt(TYPE_OFFSET);
+ mFlags = decoder.readInt(FLAGS_OFFSET);
+ if (mustHaveRequestId(mFlags)) {
+ if (mDataHeader.size < MESSAGE_WITH_REQUEST_ID_SIZE) {
+ throw new DeserializationException("Incorrect message size, expecting at least "
+ + MESSAGE_WITH_REQUEST_ID_SIZE
+ + " for a message with a request identifier, but got: " + mDataHeader.size);
+
+ }
+ mRequestId = decoder.readLong(REQUEST_ID_OFFSET);
+ } else {
+ mRequestId = 0;
+ }
+ }
+
+ /**
+ * Returns the size in bytes of the serialization of the header.
+ */
+ public int getSize() {
+ return mDataHeader.size;
+ }
+
+ /**
+ * Returns the type of the message.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the flags associated to the message.
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns if the message has the given flag.
+ */
+ public boolean hasFlag(int flag) {
+ return (mFlags & flag) == flag;
+ }
+
+ /**
+ * Returns if the message has a request id.
+ */
+ public boolean hasRequestId() {
+ return mustHaveRequestId(mFlags);
+ }
+
+ /**
+ * Return the request id for the message. Must only be called if the message has a request id.
+ */
+ public long getRequestId() {
+ assert hasRequestId();
+ return mRequestId;
+ }
+
+ /**
+ * Encode the header.
+ */
+ public void encode(Encoder encoder) {
+ encoder.encode(mDataHeader);
+ encoder.encode(getType(), TYPE_OFFSET);
+ encoder.encode(getFlags(), FLAGS_OFFSET);
+ if (hasRequestId()) {
+ encoder.encode(getRequestId(), REQUEST_ID_OFFSET);
+ }
+ }
+
+ /**
+ * Returns true if the header has the expected flags. Only considers flags this class knows
+ * about in order to allow this class to work with future version of the header format.
+ */
+ public boolean validateHeader(int expectedFlags) {
+ int knownFlags = getFlags() & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG);
+ return knownFlags == expectedFlags;
+ }
+
+ /**
+ * Returns true if the header has the expected type and flags. Only consider flags this class
+ * knows about in order to allow this class to work with future version of the header format.
+ */
+ public boolean validateHeader(int expectedType, int expectedFlags) {
+ return getType() == expectedType && validateHeader(expectedFlags);
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mDataHeader == null) ? 0 : mDataHeader.hashCode());
+ result = prime * result + mFlags;
+ result = prime * result + (int) (mRequestId ^ (mRequestId >>> 32));
+ result = prime * result + mType;
+ return result;
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this)
+ return true;
+ if (object == null)
+ return false;
+ if (getClass() != object.getClass())
+ return false;
+
+ MessageHeader other = (MessageHeader) object;
+ if (mDataHeader == null) {
+ if (other.mDataHeader != null) {
+ return false;
+ }
+ } else {
+ if (!mDataHeader.equals(other.mDataHeader)) {
+ return false;
+ }
+ }
+
+ return (mFlags == other.mFlags &&
+ mRequestId == other.mRequestId &&
+ mType == other.mType);
+ }
+
+ /**
+ * Set the request id on the message contained in the given buffer.
+ */
+ void setRequestId(ByteBuffer buffer, long requestId) {
+ assert mustHaveRequestId(buffer.getInt(FLAGS_OFFSET));
+ buffer.putLong(REQUEST_ID_OFFSET, requestId);
+ mRequestId = requestId;
+ }
+
+ /**
+ * Returns whether a message with the given flags must have a request Id.
+ */
+ private static boolean mustHaveRequestId(int flags) {
+ return (flags & (MESSAGE_EXPECTS_RESPONSE_FLAG | MESSAGE_IS_RESPONSE_FLAG)) != 0;
+ }
+
+ /**
+ * Validate that the given {@link DataHeader} can be the data header of a message header.
+ */
+ private static void validateDataHeader(DataHeader dataHeader) {
+ if (dataHeader.numFields < SIMPLE_MESSAGE_NUM_FIELDS) {
+ throw new DeserializationException(
+ "Incorrect number of fields, expecting at least " + SIMPLE_MESSAGE_NUM_FIELDS
+ + ", but got: " + dataHeader.numFields);
+ }
+ if (dataHeader.size < SIMPLE_MESSAGE_SIZE) {
+ throw new DeserializationException(
+ "Incorrect message size, expecting at least " + SIMPLE_MESSAGE_SIZE
+ + ", but got: " + dataHeader.size);
+ }
+ if (dataHeader.numFields == SIMPLE_MESSAGE_NUM_FIELDS
+ && dataHeader.size != SIMPLE_MESSAGE_SIZE) {
+ throw new DeserializationException(
+ "Incorrect message size for a message with " + SIMPLE_MESSAGE_NUM_FIELDS
+ + " fields, expecting " + SIMPLE_MESSAGE_SIZE + ", but got: "
+ + dataHeader.size);
+ }
+ if (dataHeader.numFields == MESSAGE_WITH_REQUEST_ID_NUM_FIELDS
+ && dataHeader.size != MESSAGE_WITH_REQUEST_ID_SIZE) {
+ throw new DeserializationException(
+ "Incorrect message size for a message with "
+ + MESSAGE_WITH_REQUEST_ID_NUM_FIELDS + " fields, expecting "
+ + MESSAGE_WITH_REQUEST_ID_SIZE + ", but got: " + dataHeader.size);
+ }
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
new file mode 100644
index 0000000..4223297
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import java.io.Closeable;
+
+/**
+ * A class which implements this interface can receive {@link Message} objects.
+ */
+public interface MessageReceiver extends Closeable {
+
+ /**
+ * Receive a {@link Message}. The {@link MessageReceiver} is allowed to mutate the message.
+ * Returns |true| if the message has been handled, |false| otherwise.
+ */
+ boolean accept(Message message);
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close();
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
new file mode 100644
index 0000000..e24a558
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * A {@link MessageReceiver} that can also handle the handle the response message generated from the
+ * given message.
+ */
+public interface MessageReceiverWithResponder extends MessageReceiver {
+
+ /**
+ * A variant on {@link #accept(Message)} that registers a {@link MessageReceiver}
+ * (known as the responder) to handle the response message generated from the given message. The
+ * responder's {@link #accept(Message)} method may be called as part of the call to
+ * {@link #acceptWithResponder(Message, MessageReceiver)}, or some time after its
+ * return.
+ */
+ boolean acceptWithResponder(Message message, MessageReceiver responder);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
new file mode 100644
index 0000000..ba19ae5
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
@@ -0,0 +1,31 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.MessagePipeHandle;
+
+/**
+ * A {@link Router} will handle mojo message and forward those to a {@link Connector}. It deals with
+ * parsing of headers and adding of request ids in order to be able to match a response to a
+ * request.
+ */
+public interface Router extends MessageReceiverWithResponder, HandleOwner<MessagePipeHandle> {
+
+ /**
+ * Start listening for incoming messages.
+ */
+ public void start();
+
+ /**
+ * Set the {@link MessageReceiverWithResponder} that will deserialize and use the message
+ * received from the pipe.
+ */
+ public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver);
+
+ /**
+ * Set the handle that will be notified of errors on the message pipe.
+ */
+ public void setErrorHandler(ConnectionErrorHandler errorHandler);
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
new file mode 100644
index 0000000..5b27d7e
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -0,0 +1,195 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of {@link Router}.
+ */
+public class RouterImpl implements Router {
+
+ /**
+ * {@link MessageReceiver} used as the {@link Connector} callback.
+ */
+ private class ResponderThunk implements MessageReceiver {
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ return handleIncomingMessage(message);
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.MessageReceiver#close()
+ */
+ @Override
+ public void close() {
+ handleConnectorClose();
+ }
+
+ }
+
+ /**
+ * The {@link Connector} which is connected to the handle.
+ */
+ private final Connector mConnector;
+
+ /**
+ * The {@link MessageReceiverWithResponder} that will consume the messages received from the
+ * pipe.
+ */
+ private MessageReceiverWithResponder mIncomingMessageReceiver;
+
+ /**
+ * The next id to use for a request id which needs a response. It is auto-incremented.
+ */
+ private long mNextRequestId = 1;
+
+ /**
+ * The map from request ids to {@link MessageReceiver} of request currently in flight.
+ */
+ private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageReceiver>();
+
+ /**
+ * Constructor that will use the default {@link AsyncWaiter}.
+ *
+ * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
+ */
+ public RouterImpl(MessagePipeHandle messagePipeHandle) {
+ this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
+ * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the
+ * handle.
+ */
+ public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+ mConnector = new Connector(messagePipeHandle, asyncWaiter);
+ mConnector.setIncomingMessageReceiver(new ResponderThunk());
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.Router#start()
+ */
+ @Override
+ public void start() {
+ mConnector.start();
+ }
+
+ /**
+ * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
+ */
+ @Override
+ public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
+ this.mIncomingMessageReceiver = incomingMessageReceiver;
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ // A message without responder is directly forwarded to the connector.
+ return mConnector.accept(message);
+ }
+
+ /**
+ * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+ */
+ @Override
+ public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+ // The message must have a header.
+ ServiceMessage messageWithHeader = message.asServiceMessage();
+ // Checking the message expects a response.
+ assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG);
+
+ // Compute a request id for being able to route the response.
+ long requestId = mNextRequestId++;
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ if (requestId == 0) {
+ requestId = mNextRequestId++;
+ }
+ if (mResponders.containsKey(requestId)) {
+ throw new IllegalStateException("Unable to find a new request identifier.");
+ }
+ messageWithHeader.setRequestId(requestId);
+ if (!mConnector.accept(messageWithHeader)) {
+ return false;
+ }
+ // Only keep the responder is the message has been accepted.
+ mResponders.put(requestId, responder);
+ return true;
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
+ */
+ @Override
+ public MessagePipeHandle passHandle() {
+ return mConnector.passHandle();
+ }
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ mConnector.close();
+ }
+
+ /**
+ * @see Router#setErrorHandler(ConnectionErrorHandler)
+ */
+ @Override
+ public void setErrorHandler(ConnectionErrorHandler errorHandler) {
+ mConnector.setErrorHandler(errorHandler);
+ }
+
+ /**
+ * Receive a message from the connector. Returns |true| if the message has been handled.
+ */
+ private boolean handleIncomingMessage(Message message) {
+ MessageHeader header = message.asServiceMessage().getHeader();
+ if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) {
+ if (mIncomingMessageReceiver != null) {
+ return mIncomingMessageReceiver.acceptWithResponder(message, this);
+ }
+ // If we receive a request expecting a response when the client is not
+ // listening, then we have no choice but to tear down the pipe.
+ close();
+ return false;
+ } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
+ long requestId = header.getRequestId();
+ MessageReceiver responder = mResponders.get(requestId);
+ if (responder == null) {
+ return false;
+ }
+ mResponders.remove(requestId);
+ return responder.accept(message);
+ } else {
+ if (mIncomingMessageReceiver != null) {
+ return mIncomingMessageReceiver.accept(message);
+ }
+ // OK to drop the message.
+ }
+ return false;
+ }
+
+ private void handleConnectorClose() {
+ if (mIncomingMessageReceiver != null) {
+ mIncomingMessageReceiver.close();
+ }
+ }
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
new file mode 100644
index 0000000..d4f5502
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Error that can be thrown when serializing a mojo message.
+ */
+public class SerializationException extends RuntimeException {
+
+ /**
+ * Constructs a new serialization exception with the specified detail message.
+ */
+ public SerializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new serialization exception with the specified cause.
+ */
+ public SerializationException(Exception cause) {
+ super(cause);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
new file mode 100644
index 0000000..ac62bf6
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
@@ -0,0 +1,73 @@
+// 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.
+
+package org.chromium.mojo.bindings;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Represents a {@link Message} which contains a {@link MessageHeader}. Deals with parsing the
+ * {@link MessageHeader} for a message.
+ */
+public class ServiceMessage extends Message {
+
+ private final MessageHeader mHeader;
+ private Message mPayload;
+
+ /**
+ * Reinterpret the given |message| as a message with the given |header|. The |message| must
+ * contain the |header| as the start of its raw data.
+ */
+ public ServiceMessage(Message baseMessage, MessageHeader header) {
+ super(baseMessage.getData(), baseMessage.getHandles());
+ assert header.equals(new org.chromium.mojo.bindings.MessageHeader(baseMessage));
+ this.mHeader = header;
+ }
+
+ /**
+ * Reinterpret the given |message| as a message with a header. The |message| must contain a
+ * header as the start of it's raw data, which will be parsed by this constructor.
+ */
+ ServiceMessage(Message baseMessage) {
+ this(baseMessage, new org.chromium.mojo.bindings.MessageHeader(baseMessage));
+ }
+
+ /**
+ * @see Message#asServiceMessage()
+ */
+ @Override
+ public ServiceMessage asServiceMessage() {
+ return this;
+ }
+
+ /**
+ * Returns the header of the given message. This will throw a {@link DeserializationException}
+ * if the start of the message is not a valid header.
+ */
+ public MessageHeader getHeader() {
+ return mHeader;
+ }
+
+ /**
+ * Returns the payload of the message.
+ */
+ public Message getPayload() {
+ if (mPayload == null) {
+ ByteBuffer truncatedBuffer =
+ ((ByteBuffer) getData().position(getHeader().getSize())).slice();
+ truncatedBuffer.order(ByteOrder.nativeOrder());
+ mPayload = new Message(truncatedBuffer, getHandles());
+ }
+ return mPayload;
+ }
+
+ /**
+ * Set the request identifier on the message.
+ */
+ void setRequestId(long requestId) {
+ mHeader.setRequestId(getData(), requestId);
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
new file mode 100644
index 0000000..118c991
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import java.io.Closeable;
+
+/**
+ * An implementation of closeable that doesn't do anything.
+ */
+public class SideEffectFreeCloseable implements Closeable {
+
+ /**
+ * @see java.io.Closeable#close()
+ */
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
new file mode 100644
index 0000000..017d0ef
--- /dev/null
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
@@ -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.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.system.Core;
+
+/**
+ * Base class for all mojo structs.
+ */
+public abstract class Struct {
+
+ /**
+ * The header for a mojo complex element.
+ */
+ public static final class DataHeader {
+
+ /**
+ * The size of a serialized header, in bytes.
+ */
+ public static final int HEADER_SIZE = 8;
+
+ /**
+ * The offset of the size field.
+ */
+ public static final int SIZE_OFFSET = 0;
+
+ /**
+ * The offset of the number of fields field.
+ */
+ public static final int NUM_FIELDS_OFFSET = 4;
+
+ public final int size;
+ public final int numFields;
+
+ /**
+ * Constructor.
+ */
+ public DataHeader(int size, int numFields) {
+ super();
+ this.size = size;
+ this.numFields = numFields;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + numFields;
+ result = prime * result + size;
+ return result;
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this)
+ return true;
+ if (object == null)
+ return false;
+ if (getClass() != object.getClass())
+ return false;
+
+ DataHeader other = (DataHeader) object;
+ return (numFields == other.numFields &&
+ size == other.size);
+ }
+ }
+
+ /**
+ * The base size of the struct.
+ */
+ protected final int mEncodedBaseSize;
+
+ /**
+ * Constructor.
+ */
+ protected Struct(int encodedBaseSize) {
+ this.mEncodedBaseSize = encodedBaseSize;
+ }
+
+ /**
+ * Use the given encoder to serialized this struct.
+ */
+ protected abstract void encode(Encoder encoder);
+
+ /**
+ * Returns the serialization of the struct. This method can close Handles.
+ *
+ * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+ * being encoded contains interfaces, can be |null| otherwise.
+ */
+ public Message serialize(Core core) {
+ Encoder encoder = new Encoder(core, mEncodedBaseSize);
+ encode(encoder);
+ return encoder.getMessage();
+ }
+
+ /**
+ * Returns the serialization of the struct prepended with the given header.
+ *
+ * @param header the header to prepend to the returned message.
+ * @param core the |Core| implementation used to generate handles. Only used if the |Struct|
+ * being encoded contains interfaces, can be |null| otherwise.
+ */
+ public ServiceMessage serializeWithHeader(Core core, MessageHeader header) {
+ Encoder encoder = new Encoder(core, mEncodedBaseSize + header.getSize());
+ header.encode(encoder);
+ encode(encoder);
+ return new ServiceMessage(encoder.getMessage(), header);
+ }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java
new file mode 100644
index 0000000..474de4b
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+
+/**
+ * A class which implements the {@link AsyncWaiter} allows asynchronously waiting on a background
+ * thread.
+ */
+public interface AsyncWaiter {
+
+ /**
+ * Allows cancellation of an asyncWait operation.
+ */
+ interface Cancellable {
+ /**
+ * Cancels an asyncWait operation. Has no effect if the operation has already been canceled
+ * or the callback has already been called.
+ * <p>
+ * Must be called from the same thread as {@link AsyncWaiter#asyncWait} was called from.
+ */
+ void cancel();
+ }
+
+ /**
+ * Callback passed to {@link AsyncWaiter#asyncWait}.
+ */
+ public interface Callback {
+ /**
+ * Called when the handle is ready.
+ */
+ public void onResult(int result);
+
+ /**
+ * Called when an error occurred while waiting.
+ */
+ public void onError(MojoException exception);
+ }
+
+ /**
+ * Asynchronously call wait on a background thread. The given {@link Callback} will be notified
+ * of the result of the wait on the same thread as asyncWait was called.
+ *
+ * @return a {@link Cancellable} object that can be used to cancel waiting. The cancellable
+ * should only be used on the current thread, and becomes invalid once the callback has
+ * been notified.
+ */
+ Cancellable asyncWait(Handle handle, HandleSignals signals, long deadline, Callback callback);
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
new file mode 100644
index 0000000..d6c8e7d
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -0,0 +1,195 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+import java.util.List;
+
+/**
+ * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h|
+ * for the underlying api.
+ */
+public interface Core {
+
+ /**
+ * Used to indicate an infinite deadline (timeout).
+ */
+ public static final long DEADLINE_INFINITE = -1;
+
+ /**
+ * Signals for the wait operations on handles.
+ */
+ public static class HandleSignals extends Flags<HandleSignals> {
+ /**
+ * Constructor.
+ *
+ * @param signals the serialized signals.
+ */
+ private HandleSignals(int signals) {
+ super(signals);
+ }
+
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_READABLE = 1 << 0;
+ private static final int FLAG_WRITABLE = 1 << 1;
+
+ /**
+ * Immutable signals.
+ */
+ public static final HandleSignals NONE = HandleSignals.none().immutable();
+ public static final HandleSignals READABLE =
+ HandleSignals.none().setReadable(true).immutable();
+ public static final HandleSignals WRITABLE =
+ HandleSignals.none().setWritable(true).immutable();
+
+ /**
+ * Change the readable bit of this signal.
+ *
+ * @param readable the new value of the readable bit.
+ * @return this.
+ */
+ public HandleSignals setReadable(boolean readable) {
+ return setFlag(FLAG_READABLE, readable);
+ }
+
+ /**
+ * Change the writable bit of this signal.
+ *
+ * @param writable the new value of the writable bit.
+ * @return this.
+ */
+ public HandleSignals setWritable(boolean writable) {
+ return setFlag(FLAG_WRITABLE, writable);
+ }
+
+ /**
+ * @return a signal with no bit set.
+ */
+ public static HandleSignals none() {
+ return new HandleSignals(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * @return a platform-dependent monotonically increasing tick count representing "right now."
+ */
+ public long getTimeTicksNow();
+
+ /**
+ * Waits on the given |handle| until the state indicated by |signals| is satisfied or until
+ * |deadline| has passed.
+ *
+ * @return |MojoResult.OK| if some signal in |signals| was satisfied (or is already satisfied).
+ * <p>
+ * |MojoResult.DEADLINE_EXCEEDED| if the deadline has passed without any of the signals
+ * being satisfied.
+ * <p>
+ * |MojoResult.CANCELLED| if |handle| is closed concurrently by another thread.
+ * <p>
+ * |MojoResult.FAILED_PRECONDITION| if it is or becomes impossible that any flag in
+ * |signals| will ever be satisfied (for example, if the other endpoint is close).
+ */
+ public int wait(Handle handle, HandleSignals signals, long deadline);
+
+ /**
+ * Result for the |waitMany| method.
+ */
+ public static class WaitManyResult {
+
+ /**
+ * See |wait| for the different possible values.
+ */
+ private int mMojoResult;
+ /**
+ * If |mojoResult| is |MojoResult.OK|, |handleIndex| is the index of the handle for which
+ * some flag was satisfied (or is already satisfied). If |mojoResult| is
+ * |MojoResult.CANCELLED| or |MojoResult.FAILED_PRECONDITION|, |handleIndex| is the index of
+ * the handle for which the issue occurred.
+ */
+ private int mHandleIndex;
+
+ /**
+ * @return the mojoResult
+ */
+ public int getMojoResult() {
+ return mMojoResult;
+ }
+
+ /**
+ * @param mojoResult the mojoResult to set
+ */
+ public void setMojoResult(int mojoResult) {
+ mMojoResult = mojoResult;
+ }
+
+ /**
+ * @return the handleIndex
+ */
+ public int getHandleIndex() {
+ return mHandleIndex;
+ }
+
+ /**
+ * @param handleIndex the handleIndex to set
+ */
+ public void setHandleIndex(int handleIndex) {
+ mHandleIndex = handleIndex;
+ }
+ }
+
+ /**
+ * Waits on handle in |handles| for at least one of them to satisfy the associated
+ * |HandleSignals|, or until |deadline| has passed.
+ *
+ * @returns a |WaitManyResult|.
+ */
+ public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline);
+
+ /**
+ * Creates a message pipe, which is a bidirectional communication channel for framed data (i.e.,
+ * messages), with the given options. Messages can contain plain data and/or Mojo handles.
+ *
+ * @return the set of handles for the two endpoints (ports) of the message pipe.
+ */
+ public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe(
+ MessagePipeHandle.CreateOptions options);
+
+ /**
+ * Creates a data pipe, which is a unidirectional communication channel for unframed data, with
+ * the given options. Data is unframed, but must come as (multiples of) discrete elements, of
+ * the size given in |options|. See |DataPipe.CreateOptions| for a description of the different
+ * options available for data pipes. |options| may be set to null for a data pipe with the
+ * default options (which will have an element size of one byte and have some system-dependent
+ * capacity).
+ *
+ * @return the set of handles for the two endpoints of the data pipe.
+ */
+ public Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> createDataPipe(
+ DataPipe.CreateOptions options);
+
+ /**
+ * Creates a buffer that can be shared between applications (by duplicating the handle -- see
+ * |SharedBufferHandle.duplicate()| -- and passing it over a message pipe). To access the
+ * buffer, one must call |SharedBufferHandle.map|.
+ *
+ * @return the new |SharedBufferHandle|.
+ */
+ public SharedBufferHandle createSharedBuffer(SharedBufferHandle.CreateOptions options,
+ long numBytes);
+
+ /**
+ * Acquires a handle from the native side. The handle will be owned by the returned object and
+ * must not be closed outside of it.
+ *
+ * @return a new {@link UntypedHandle} representing the native handle.
+ */
+ public UntypedHandle acquireNativeHandle(int handle);
+
+ /**
+ * Returns a default implementation of {@link AsyncWaiter}.
+ */
+ public AsyncWaiter getDefaultAsyncWaiter();
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
new file mode 100644
index 0000000..4cd74e1
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
@@ -0,0 +1,335 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Interface for data pipes. A data pipe is a unidirectional communication channel for unframed
+ * data. Data is unframed, but must come as (multiples of) discrete elements, of the size given at
+ * creation time.
+ */
+public interface DataPipe {
+
+ /**
+ * Flags for the data pipe creation operation.
+ */
+ public static class CreateFlags extends Flags<CreateFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_MAY_DISCARD = 1 << 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected CreateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the may-discard bit of this flag. This indicates that the data pipe may discard
+ * data for whatever reason; best-effort delivery. In particular, if the capacity is
+ * reached, old data may be discard to make room for new data.
+ *
+ * @param mayDiscard the new value of the may-discard bit.
+ * @return this.
+ */
+ public CreateFlags setMayDiscard(boolean mayDiscard) {
+ return setFlag(FLAG_MAY_DISCARD, mayDiscard);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static CreateFlags none() {
+ return new CreateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify creation parameters for a data pipe to |Core.createDataPipe()|.
+ */
+ public static class CreateOptions {
+
+ /**
+ * Used to specify different modes of operation, see |DataPipe.CreateFlags|.
+ */
+ private CreateFlags mFlags = CreateFlags.none();
+ /**
+ * The size of an element, in bytes. All transactions and buffers will consist of an
+ * integral number of elements. Must be nonzero.
+ */
+ private int mElementNumBytes;
+ /**
+ * The capacity of the data pipe, in number of bytes; must be a multiple of
+ * |element_num_bytes|. The data pipe will always be able to queue AT LEAST this much data.
+ * Set to zero to opt for a system-dependent automatically-calculated capacity (which will
+ * always be at least one element).
+ */
+ private int mCapacityNumBytes;
+
+ /**
+ * @return the flags
+ */
+ public CreateFlags getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * @return the elementNumBytes
+ */
+ public int getElementNumBytes() {
+ return mElementNumBytes;
+ }
+
+ /**
+ * @param elementNumBytes the elementNumBytes to set
+ */
+ public void setElementNumBytes(int elementNumBytes) {
+ mElementNumBytes = elementNumBytes;
+ }
+
+ /**
+ * @return the capacityNumBytes
+ */
+ public int getCapacityNumBytes() {
+ return mCapacityNumBytes;
+ }
+
+ /**
+ * @param capacityNumBytes the capacityNumBytes to set
+ */
+ public void setCapacityNumBytes(int capacityNumBytes) {
+ mCapacityNumBytes = capacityNumBytes;
+ }
+
+ }
+
+ /**
+ * Flags for the write operations on MessagePipeHandle .
+ */
+ public static class WriteFlags extends Flags<WriteFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_ALL_OR_NONE = 1 << 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final WriteFlags NONE = WriteFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ private WriteFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the all-or-none bit of those flags. If set, write either all the elements
+ * requested or none of them.
+ *
+ * @param allOrNone the new value of all-or-none bit.
+ * @return this.
+ */
+ public WriteFlags setAllOrNone(boolean allOrNone) {
+ return setFlag(FLAG_ALL_OR_NONE, allOrNone);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static WriteFlags none() {
+ return new WriteFlags(FLAG_NONE);
+ }
+ }
+
+ /**
+ * Flags for the read operations on MessagePipeHandle.
+ */
+ public static class ReadFlags extends Flags<ReadFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_ALL_OR_NONE = 1 << 0;
+ private static final int FLAG_QUERY = 1 << 2;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final ReadFlags NONE = ReadFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ private ReadFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the all-or-none bit of this flag. If set, read (or discard) either the requested
+ * number of elements or none.
+ *
+ * @param allOrNone the new value of the all-or-none bit.
+ * @return this.
+ */
+ public ReadFlags setAllOrNone(boolean allOrNone) {
+ return setFlag(FLAG_ALL_OR_NONE, allOrNone);
+ }
+
+ /**
+ * Change the query bit of this flag. If set query the number of elements available to read.
+ * Mutually exclusive with |dicard| and |allOrNone| is ignored if this flag is set.
+ *
+ * @param query the new value of the query bit.
+ * @return this.
+ */
+ public ReadFlags query(boolean query) {
+ return setFlag(FLAG_QUERY, query);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static ReadFlags none() {
+ return new ReadFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Handle for the producer part of a data pipe.
+ */
+ public static interface ProducerHandle extends Handle {
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public ProducerHandle pass();
+
+ /**
+ * Writes the given data to the data pipe producer. |elements| points to data; the buffer
+ * must be a direct ByteBuffer and the limit should be a multiple of the data pipe's element
+ * size. If |allOrNone| is set in |flags|, either all the data will be written or none is.
+ * <p>
+ * On success, returns the amount of data that was actually written.
+ * <p>
+ * Note: If the data pipe has the "may discard" option flag (specified on creation), this
+ * will discard as much data as required to write the given data, starting with the earliest
+ * written data that has not been consumed. However, even with "may discard", if the buffer
+ * limit is greater than the data pipe's capacity (and |allOrNone| is not set), this will
+ * write the maximum amount possible (namely, the data pipe's capacity) and return that
+ * amount. It will *not* discard data from |elements|.
+ *
+ * @return number of written bytes.
+ */
+ public int writeData(ByteBuffer elements, WriteFlags flags);
+
+ /**
+ * Begins a two-phase write to the data pipe producer . On success, returns a |ByteBuffer|
+ * to which the caller can write. If flags has |allOrNone| set, then the buffer capacity
+ * will be at least as large as |numBytes|, which must also be a multiple of the element
+ * size (if |allOrNone| is not set, |numBytes| is ignored and the caller must check the
+ * capacity of the buffer).
+ * <p>
+ * During a two-phase write, this handle is *not* writable. E.g., if another thread tries to
+ * write to it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread can
+ * then wait for this handle to become writable again.
+ * <p>
+ * Once the caller has finished writing data to the buffer, it should call |endWriteData()|
+ * to specify the amount written and to complete the two-phase write.
+ * <p>
+ * Note: If the data pipe has the "may discard" option flag (specified on creation) and
+ * |flags| has |allOrNone| set, this may discard some data.
+ *
+ * @return The buffer to write to.
+ */
+ public ByteBuffer beginWriteData(int numBytes, WriteFlags flags);
+
+ /**
+ * Ends a two-phase write to the data pipe producer that was begun by a call to
+ * |beginWriteData()| on the same handle. |numBytesWritten| should indicate the amount of
+ * data actually written; it must be less than or equal to the capacity of the buffer
+ * returned by |beginWriteData()| and must be a multiple of the element size. The buffer
+ * returned from |beginWriteData()| must have been filled with exactly |numBytesWritten|
+ * bytes of data.
+ * <p>
+ * On failure, the two-phase write (if any) is ended (so the handle may become writable
+ * again, if there's space available) but no data written to the buffer is "put into" the
+ * data pipe.
+ */
+ public void endWriteData(int numBytesWritten);
+ }
+
+ /**
+ * Handle for the consumer part of a data pipe.
+ */
+ public static interface ConsumerHandle extends Handle {
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public ConsumerHandle pass();
+
+ /**
+ * Discards data on the data pie consumer. This method discards up to |numBytes| (which
+ * again be a multiple of the element size) bytes of data, returning the amount actually
+ * discarded. if |flags| has |allOrNone|, it will either discard exactly |numBytes| bytes of
+ * data or none. In this case, |query| must not be set.
+ */
+ public int discardData(int numBytes, ReadFlags flags);
+
+ /**
+ * Reads data from the data pipe consumer. May also be used to query the amount of data
+ * available. If |flags| has not |query| set, this tries to read up to |elements| capacity
+ * (which must be a multiple of the data pipe's element size) bytes of data to |elements|
+ * and returns the amount actually read. |elements| must be a direct ByteBuffer. If flags
+ * has |allOrNone| set, it will either read exactly |elements| capacity bytes of data or
+ * none.
+ * <p>
+ * If flags has |query| set, it queries the amount of data available, returning the number
+ * of bytes available. In this case |allOrNone| is ignored, as are |elements|.
+ */
+ public int readData(ByteBuffer elements, ReadFlags flags);
+
+ /**
+ * Begins a two-phase read from the data pipe consumer. On success, returns a |ByteBuffer|
+ * from which the caller can read up to its limit bytes of data. If flags has |allOrNone|
+ * set, then the limit will be at least as large as |numBytes|, which must also be a
+ * multiple of the element size (if |allOrNone| is not set, |numBytes| is ignored). |flags|
+ * must not have |query| set.
+ * <p>
+ * During a two-phase read, this handle is *not* readable. E.g., if another thread tries to
+ * read from it, it will throw a |MojoException| with code |MojoResult.BUSY|; that thread
+ * can then wait for this handle to become readable again.
+ * <p>
+ * Once the caller has finished reading data from the buffer, it should call |endReadData()|
+ * to specify the amount read and to complete the two-phase read.
+ */
+ public ByteBuffer beginReadData(int numBytes, ReadFlags flags);
+
+ /**
+ * Ends a two-phase read from the data pipe consumer that was begun by a call to
+ * |beginReadData()| on the same handle. |numBytesRead| should indicate the amount of data
+ * actually read; it must be less than or equal to the limit of the buffer returned by
+ * |beginReadData()| and must be a multiple of the element size.
+ * <p>
+ * On failure, the two-phase read (if any) is ended (so the handle may become readable
+ * again) but no data is "removed" from the data pipe.
+ */
+ public void endReadData(int numBytesRead);
+ }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
new file mode 100644
index 0000000..dd2b00a
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+/**
+ * Base class for bit field used as flags.
+ *
+ * @param <F> the type of the flags.
+ */
+public abstract class Flags<F extends Flags<F>> {
+ private int mFlags;
+ private boolean mImmutable;
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ protected Flags(int flags) {
+ mImmutable = false;
+ mFlags = flags;
+ }
+
+ /**
+ * @return the computed flag.
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Change the given bit of this flag.
+ *
+ * @param value the new value of given bit.
+ * @return this.
+ */
+ protected F setFlag(int flag, boolean value) {
+ if (mImmutable) {
+ throw new UnsupportedOperationException("Flags is immutable.");
+ }
+ if (value) {
+ mFlags |= flag;
+ } else {
+ mFlags &= ~flag;
+ }
+ @SuppressWarnings("unchecked")
+ F f = (F) this;
+ return f;
+ }
+
+ /**
+ * Makes this flag immutable. This is a non-reversable operation.
+ */
+ protected F immutable() {
+ mImmutable = true;
+ @SuppressWarnings("unchecked")
+ F f = (F) this;
+ return f;
+ }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
new file mode 100644
index 0000000..907e2b9
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -0,0 +1,59 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+import java.io.Closeable;
+
+/**
+ * A generic mojo handle.
+ */
+public interface Handle extends Closeable {
+
+ /**
+ * Closes the given |handle|.
+ * <p>
+ * Concurrent operations on |handle| may succeed (or fail as usual) if they happen before the
+ * close, be cancelled with result |MojoResult.CANCELLED| if they properly overlap (this is
+ * likely the case with |wait()|, etc.), or fail with |MojoResult.INVALID_ARGUMENT| if they
+ * happen after.
+ */
+ @Override
+ public void close();
+
+ /**
+ * @see Core#wait(Handle, Core.HandleSignals, long)
+ */
+ public int wait(Core.HandleSignals signals, long deadline);
+
+ /**
+ * @return whether the handle is valid. A handle is valid until it has been explicitly closed or
+ * send through a message pipe via |MessagePipeHandle.writeMessage|.
+ */
+ public boolean isValid();
+
+ /**
+ * Converts this handle into an {@link UntypedHandle}, invalidating this handle.
+ */
+ public UntypedHandle toUntypedHandle();
+
+ /**
+ * Returns the {@link Core} implementation for this handle. Can be null if this handle is
+ * invalid.
+ */
+ public Core getCore();
+
+ /**
+ * Passes ownership of the handle from this handle to the newly created Handle object,
+ * invalidating this handle object in the process.
+ */
+ public Handle pass();
+
+ /**
+ * Releases the native handle backed by this {@link Handle}. The caller owns the handle and must
+ * close it.
+ */
+ public int releaseNativeHandle();
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
new file mode 100644
index 0000000..12f0457
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -0,0 +1,219 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A handle that will always be invalid.
+ */
+public class InvalidHandle implements UntypedHandle, MessagePipeHandle, ConsumerHandle,
+ ProducerHandle, SharedBufferHandle {
+
+ /**
+ * Instance singleton.
+ */
+ public static final InvalidHandle INSTANCE = new InvalidHandle();
+
+ /**
+ * Private constructor.
+ */
+ private InvalidHandle() {
+ }
+
+ /**
+ * @see Handle#close()
+ */
+ @Override
+ public void close() {
+ // Do nothing.
+ }
+
+ /**
+ * @see Handle#wait(Core.HandleSignals, long)
+ */
+ @Override
+ public int wait(HandleSignals signals, long deadline) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see Handle#isValid()
+ */
+ @Override
+ public boolean isValid() {
+ return false;
+ }
+
+ /**
+ * @see Handle#getCore()
+ */
+ @Override
+ public Core getCore() {
+ return null;
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public InvalidHandle pass() {
+ return this;
+ }
+
+ /**
+ * @see Handle#toUntypedHandle()
+ */
+ @Override
+ public UntypedHandle toUntypedHandle() {
+ return this;
+ }
+
+ /**
+ * @see Handle#releaseNativeHandle()
+ */
+ @Override
+ public int releaseNativeHandle() {
+ return -1;
+ }
+
+ /**
+ * @see UntypedHandle#toMessagePipeHandle()
+ */
+ @Override
+ public MessagePipeHandle toMessagePipeHandle() {
+ return this;
+ }
+
+ /**
+ * @see UntypedHandle#toDataPipeConsumerHandle()
+ */
+ @Override
+ public ConsumerHandle toDataPipeConsumerHandle() {
+ return this;
+ }
+
+ /**
+ * @see UntypedHandle#toDataPipeProducerHandle()
+ */
+ @Override
+ public ProducerHandle toDataPipeProducerHandle() {
+ return this;
+ }
+
+ /**
+ * @see UntypedHandle#toSharedBufferHandle()
+ */
+ @Override
+ public SharedBufferHandle toSharedBufferHandle() {
+ return this;
+ }
+
+ /**
+ * @see SharedBufferHandle#duplicate(SharedBufferHandle.DuplicateOptions)
+ */
+ @Override
+ public SharedBufferHandle duplicate(DuplicateOptions options) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see SharedBufferHandle#map(long, long, SharedBufferHandle.MapFlags)
+ */
+ @Override
+ public ByteBuffer map(long offset, long numBytes, MapFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see SharedBufferHandle#unmap(java.nio.ByteBuffer)
+ */
+ @Override
+ public void unmap(ByteBuffer buffer) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ProducerHandle#writeData(java.nio.ByteBuffer, DataPipe.WriteFlags)
+ */
+ @Override
+ public int writeData(ByteBuffer elements, DataPipe.WriteFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ProducerHandle#beginWriteData(int, DataPipe.WriteFlags)
+ */
+ @Override
+ public ByteBuffer beginWriteData(int numBytes,
+ DataPipe.WriteFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ProducerHandle#endWriteData(int)
+ */
+ @Override
+ public void endWriteData(int numBytesWritten) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#discardData(int, DataPipe.ReadFlags)
+ */
+ @Override
+ public int discardData(int numBytes, DataPipe.ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#readData(java.nio.ByteBuffer, DataPipe.ReadFlags)
+ */
+ @Override
+ public int readData(ByteBuffer elements, DataPipe.ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
+ */
+ @Override
+ public ByteBuffer beginReadData(int numBytes,
+ DataPipe.ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see DataPipe.ConsumerHandle#endReadData(int)
+ */
+ @Override
+ public void endReadData(int numBytesRead) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see MessagePipeHandle#writeMessage(java.nio.ByteBuffer, java.util.List,
+ * MessagePipeHandle.WriteFlags)
+ */
+ @Override
+ public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+ /**
+ * @see MessagePipeHandle#readMessage(java.nio.ByteBuffer, int, MessagePipeHandle.ReadFlags)
+ */
+ @Override
+ public ReadMessageResult readMessage(ByteBuffer bytes, int maxNumberOfHandles,
+ ReadFlags flags) {
+ throw new MojoException(MojoResult.INVALID_ARGUMENT);
+ }
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
new file mode 100644
index 0000000..60ac682
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
@@ -0,0 +1,242 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Message pipes are bidirectional communication channel for framed data (i.e., messages). Messages
+ * can contain plain data and/or Mojo handles.
+ */
+public interface MessagePipeHandle extends Handle {
+
+ /**
+ * Flags for the message pipe creation operation.
+ */
+ public static class CreateFlags extends Flags<CreateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected CreateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static CreateFlags none() {
+ return new CreateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify creation parameters for a message pipe to |Core#createMessagePipe()|.
+ */
+ public static class CreateOptions {
+ private CreateFlags mFlags = CreateFlags.NONE;
+
+ /**
+ * @return the flags
+ */
+ public CreateFlags getFlags() {
+ return mFlags;
+ }
+
+ }
+
+ /**
+ * Flags for the write operations on MessagePipeHandle .
+ */
+ public static class WriteFlags extends Flags<WriteFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with no bit set.
+ */
+ public static final WriteFlags NONE = WriteFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ private WriteFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static WriteFlags none() {
+ return new WriteFlags(FLAG_NONE);
+ }
+ }
+
+ /**
+ * Flags for the read operations on MessagePipeHandle.
+ */
+ public static class ReadFlags extends Flags<ReadFlags> {
+ private static final int FLAG_NONE = 0;
+ private static final int FLAG_MAY_DISCARD = 1 << 0;
+
+ /**
+ * Immutable flag with no bit set.
+ */
+ public static final ReadFlags NONE = ReadFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flag.
+ */
+ private ReadFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * Change the may-discard bit of this flag. If set, if the message is unable to be read for
+ * whatever reason (e.g., the caller-supplied buffer is too small), discard the message
+ * (i.e., simply dequeue it).
+ *
+ * @param mayDiscard the new value of the may-discard bit.
+ * @return this.
+ */
+ public ReadFlags setMayDiscard(boolean mayDiscard) {
+ return setFlag(FLAG_MAY_DISCARD, mayDiscard);
+ }
+
+ /**
+ * @return a flag with no bit set.
+ */
+ public static ReadFlags none() {
+ return new ReadFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Result of the |readMessage| method.
+ */
+ public static class ReadMessageResult {
+ /**
+ * The MojoResult value of the read call.
+ */
+ private int mMojoResult;
+ /**
+ * If a message was read, the size in bytes of the message, otherwise the size in bytes of
+ * the next message.
+ */
+ private int mMessageSize;
+ /**
+ * If a message was read, the number of handles contained in the message, otherwise the
+ * number of handles contained in the next message.
+ */
+ private int mHandlesCount;
+ /**
+ * If a message was read, the handles contained in the message, undefined otherwise.
+ */
+ private List<UntypedHandle> mHandles;
+
+ /**
+ * @return the mojoResult
+ */
+ public int getMojoResult() {
+ return mMojoResult;
+ }
+
+ /**
+ * @param mojoResult the mojoResult to set
+ */
+ public void setMojoResult(int mojoResult) {
+ mMojoResult = mojoResult;
+ }
+
+ /**
+ * @return the messageSize
+ */
+ public int getMessageSize() {
+ return mMessageSize;
+ }
+
+ /**
+ * @param messageSize the messageSize to set
+ */
+ public void setMessageSize(int messageSize) {
+ mMessageSize = messageSize;
+ }
+
+ /**
+ * @return the handlesCount
+ */
+ public int getHandlesCount() {
+ return mHandlesCount;
+ }
+
+ /**
+ * @param handlesCount the handlesCount to set
+ */
+ public void setHandlesCount(int handlesCount) {
+ mHandlesCount = handlesCount;
+ }
+
+ /**
+ * @return the handles
+ */
+ public List<UntypedHandle> getHandles() {
+ return mHandles;
+ }
+
+ /**
+ * @param handles the handles to set
+ */
+ public void setHandles(List<UntypedHandle> handles) {
+ mHandles = handles;
+ }
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public MessagePipeHandle pass();
+
+ /**
+ * Writes a message to the message pipe endpoint, with message data specified by |bytes| and
+ * attached handles specified by |handles|, and options specified by |flags|. If there is no
+ * message data, |bytes| may be null, otherwise it must be a direct ByteBuffer. If there are no
+ * attached handles, |handles| may be null.
+ * <p>
+ * If handles are attached, on success the handles will no longer be valid (the receiver will
+ * receive equivalent, but logically different, handles). Handles to be sent should not be in
+ * simultaneous use (e.g., on another thread).
+ */
+ void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags);
+
+ /**
+ * Reads a message from the message pipe endpoint; also usable to query the size of the next
+ * message or discard the next message. |bytes| indicate the buffer/buffer size to receive the
+ * message data (if any) and |maxNumberOfHandles| indicate the maximum handle count to receive
+ * the attached handles (if any). |bytes| is optional. If null, |maxNumberOfHandles| must be
+ * zero, and the return value will indicate the size of the next message. If non-null, it must
+ * be a direct ByteBuffer and the return value will indicate if the message was read or not. If
+ * the message was read its content will be in |bytes|, and the attached handles will be in the
+ * return value. Partial reads are NEVER done. Either a full read is done and |wasMessageRead|
+ * will be true, or the read is NOT done and |wasMessageRead| will be false (if |mayDiscard| was
+ * set, the message is also discarded in this case).
+ */
+ ReadMessageResult readMessage(ByteBuffer bytes, int maxNumberOfHandles,
+ ReadFlags flags);
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
new file mode 100644
index 0000000..e06f647
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
@@ -0,0 +1,36 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+/**
+ * Exception for the core mojo API.
+ */
+public class MojoException extends RuntimeException {
+
+ private final int mCode;
+
+ /**
+ * Constructor.
+ */
+ public MojoException(int code) {
+ mCode = code;
+ }
+
+ /**
+ * The mojo result code associated with this exception. See {@link MojoResult} for possible
+ * values.
+ */
+ public int getMojoResult() {
+ return mCode;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "MojoResult(" + mCode + "): " + MojoResult.describe(mCode);
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
new file mode 100644
index 0000000..a829c83
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
@@ -0,0 +1,82 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+/**
+ * The different mojo result codes.
+ */
+public final class MojoResult {
+ public static final int OK = 0;
+ public static final int CANCELLED = -1;
+ public static final int UNKNOWN = -2;
+ public static final int INVALID_ARGUMENT = -3;
+ public static final int DEADLINE_EXCEEDED = -4;
+ public static final int NOT_FOUND = -5;
+ public static final int ALREADY_EXISTS = -6;
+ public static final int PERMISSION_DENIED = -7;
+ public static final int RESOURCE_EXHAUSTED = -8;
+ public static final int FAILED_PRECONDITION = -9;
+ public static final int ABORTED = -10;
+ public static final int OUT_OF_RANGE = -11;
+ public static final int UNIMPLEMENTED = -12;
+ public static final int INTERNAL = -13;
+ public static final int UNAVAILABLE = -14;
+ public static final int DATA_LOSS = -15;
+ public static final int BUSY = -16;
+ public static final int SHOULD_WAIT = -17;
+
+ /**
+ * never instantiate.
+ */
+ private MojoResult() {
+ }
+
+ /**
+ * Describes the given result code.
+ */
+ public static String describe(int mCode) {
+ switch (mCode) {
+ case OK:
+ return "OK";
+ case CANCELLED:
+ return "CANCELLED";
+ case UNKNOWN:
+ return "UNKNOWN";
+ case INVALID_ARGUMENT:
+ return "INVALID_ARGUMENT";
+ case DEADLINE_EXCEEDED:
+ return "DEADLINE_EXCEEDED";
+ case NOT_FOUND:
+ return "NOT_FOUND";
+ case ALREADY_EXISTS:
+ return "ALREADY_EXISTS";
+ case PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case RESOURCE_EXHAUSTED:
+ return "RESOURCE_EXHAUSTED";
+ case FAILED_PRECONDITION:
+ return "FAILED_PRECONDITION";
+ case ABORTED:
+ return "ABORTED";
+ case OUT_OF_RANGE:
+ return "OUT_OF_RANGE";
+ case UNIMPLEMENTED:
+ return "UNIMPLEMENTED";
+ case INTERNAL:
+ return "INTERNAL";
+ case UNAVAILABLE:
+ return "UNAVAILABLE";
+ case DATA_LOSS:
+ return "DATA_LOSS";
+ case BUSY:
+ return "BUSY";
+ case SHOULD_WAIT:
+ return "SHOULD_WAIT";
+ default:
+ return "UNKNOWN";
+ }
+
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
new file mode 100644
index 0000000..2ead020
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
@@ -0,0 +1,67 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+
+/**
+ * A pair of object.
+ *
+ * @param <F> Type of the first element.
+ * @param <S> Type of the second element.
+ */
+public class Pair<F, S> {
+
+ public final F first;
+ public final S second;
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param first the first element of the pair.
+ * @param second the second element of the pair.
+ */
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * equals() that handles null values.
+ */
+ private boolean equals(Object o1, Object o2) {
+ return o1 == null ? o2 == null : o1.equals(o2);
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Pair)) {
+ return false;
+ }
+ Pair<?, ?> p = (Pair<?, ?>) o;
+ return equals(first, p.first) && equals(second, p.second);
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+ }
+
+ /**
+ * Helper method for creating a pair.
+ *
+ * @param a the first element of the pair.
+ * @param b the second element of the pair.
+ * @return the pair containing a and b.
+ */
+ public static <A, B> Pair<A, B> create(A a, B b) {
+ return new Pair<A, B>(a, b);
+ }
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
new file mode 100644
index 0000000..df317d1
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
@@ -0,0 +1,160 @@
+// 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.
+
+package org.chromium.mojo.system;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A buffer that can be shared between applications.
+ */
+public interface SharedBufferHandle extends Handle {
+
+ /**
+ * Flags for the shared buffer creation operation.
+ */
+ public static class CreateFlags extends Flags<CreateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final CreateFlags NONE = CreateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected CreateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static CreateFlags none() {
+ return new CreateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify creation parameters for a shared buffer to |Core#createSharedBuffer()|.
+ */
+ public static class CreateOptions {
+ private CreateFlags mFlags = CreateFlags.NONE;
+
+ /**
+ * @return the flags
+ */
+ public CreateFlags getFlags() {
+ return mFlags;
+ }
+
+ }
+
+ /**
+ * Flags for the shared buffer duplication operation.
+ */
+ public static class DuplicateFlags extends Flags<DuplicateFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final DuplicateFlags NONE = DuplicateFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected DuplicateFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static DuplicateFlags none() {
+ return new DuplicateFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * Used to specify parameters in duplicating access to a shared buffer to
+ * |SharedBufferHandle#duplicate|
+ */
+ public static class DuplicateOptions {
+ private DuplicateFlags mFlags = DuplicateFlags.NONE;
+
+ /**
+ * @return the flags
+ */
+ public DuplicateFlags getFlags() {
+ return mFlags;
+ }
+
+ }
+
+ /**
+ * Flags for the shared buffer map operation.
+ */
+ public static class MapFlags extends Flags<MapFlags> {
+ private static final int FLAG_NONE = 0;
+
+ /**
+ * Immutable flag with not bit set.
+ */
+ public static final MapFlags NONE = MapFlags.none().immutable();
+
+ /**
+ * Dedicated constructor.
+ *
+ * @param flags initial value of the flags.
+ */
+ protected MapFlags(int flags) {
+ super(flags);
+ }
+
+ /**
+ * @return flags with no bit set.
+ */
+ public static MapFlags none() {
+ return new MapFlags(FLAG_NONE);
+ }
+
+ }
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public SharedBufferHandle pass();
+
+ /**
+ * Duplicates the handle. This creates another handle (returned on success), which can then be
+ * sent to another application over a message pipe, while retaining access to this handle (and
+ * any mappings that it may have).
+ */
+ public SharedBufferHandle duplicate(DuplicateOptions options);
+
+ /**
+ * Map the part (at offset |offset| of length |numBytes|) of the buffer given by this handle
+ * into memory. |offset + numBytes| must be less than or equal to the size of the buffer. On
+ * success, the returned buffer points to memory with the requested part of the buffer. A single
+ * buffer handle may have multiple active mappings (possibly depending on the buffer type). The
+ * permissions (e.g., writable or executable) of the returned memory may depend on the
+ * properties of the buffer and properties attached to the buffer handle as well as |flags|.
+ */
+ public ByteBuffer map(long offset, long numBytes, MapFlags flags);
+
+ /**
+ * Unmap a buffer pointer that was mapped by |map()|.
+ */
+ public void unmap(ByteBuffer buffer);
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
new file mode 100644
index 0000000..199b0a1
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
@@ -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.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+
+/**
+ * A mojo handle of unknown type. This handle can be typed by using one of its methods, which will
+ * return a handle of the requested type and invalidate this object. No validation is made when the
+ * conversion operation is called.
+ */
+public interface UntypedHandle extends Handle {
+
+ /**
+ * @see org.chromium.mojo.system.Handle#pass()
+ */
+ @Override
+ public UntypedHandle pass();
+
+ /**
+ * Returns the underlying handle, as a {@link MessagePipeHandle}, invalidating this
+ * representation.
+ */
+ public MessagePipeHandle toMessagePipeHandle();
+
+ /**
+ * Returns the underlying handle, as a {@link ConsumerHandle}, invalidating this representation.
+ */
+ public ConsumerHandle toDataPipeConsumerHandle();
+
+ /**
+ * Returns the underlying handle, as a {@link ProducerHandle}, invalidating this representation.
+ */
+ public ProducerHandle toDataPipeProducerHandle();
+
+ /**
+ * Returns the underlying handle, as a {@link SharedBufferHandle}, invalidating this
+ * representation.
+ */
+ public SharedBufferHandle toSharedBufferHandle();
+
+}
diff --git a/mojo/public/js/bindings/BUILD.gn b/mojo/public/js/bindings/BUILD.gn
new file mode 100644
index 0000000..0f305e9
--- /dev/null
+++ b/mojo/public/js/bindings/BUILD.gn
@@ -0,0 +1,10 @@
+# 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.
+
+source_set("bindings") {
+ sources = [
+ "constants.cc",
+ "constants.h",
+ ]
+}
diff --git a/mojo/public/js/bindings/buffer.js b/mojo/public/js/bindings/buffer.js
new file mode 100644
index 0000000..d74fef6
--- /dev/null
+++ b/mojo/public/js/bindings/buffer.js
@@ -0,0 +1,156 @@
+// 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("mojo/public/js/bindings/buffer", function() {
+
+ var kHostIsLittleEndian = (function () {
+ var endianArrayBuffer = new ArrayBuffer(2);
+ var endianUint8Array = new Uint8Array(endianArrayBuffer);
+ var endianUint16Array = new Uint16Array(endianArrayBuffer);
+ endianUint16Array[0] = 1;
+ return endianUint8Array[0] == 1;
+ })();
+
+ var kHighWordMultiplier = 0x100000000;
+
+ function Buffer(sizeOrArrayBuffer) {
+ if (sizeOrArrayBuffer instanceof ArrayBuffer)
+ this.arrayBuffer = sizeOrArrayBuffer;
+ else
+ this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer);
+
+ this.dataView = new DataView(this.arrayBuffer);
+ this.next = 0;
+ }
+
+ Object.defineProperty(Buffer.prototype, "byteLength", {
+ get: function() { return this.arrayBuffer.byteLength; }
+ });
+
+ Buffer.prototype.alloc = function(size) {
+ var pointer = this.next;
+ this.next += size;
+ if (this.next > this.byteLength) {
+ var newSize = (1.5 * (this.byteLength + size)) | 0;
+ this.grow(newSize);
+ }
+ return pointer;
+ };
+
+ function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
+ (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
+ }
+
+ Buffer.prototype.grow = function(size) {
+ var newArrayBuffer = new ArrayBuffer(size);
+ copyArrayBuffer(newArrayBuffer, this.arrayBuffer);
+ this.arrayBuffer = newArrayBuffer;
+ this.dataView = new DataView(this.arrayBuffer);
+ };
+
+ Buffer.prototype.trim = function() {
+ this.arrayBuffer = this.arrayBuffer.slice(0, this.next);
+ this.dataView = new DataView(this.arrayBuffer);
+ };
+
+ Buffer.prototype.getUint8 = function(offset) {
+ return this.dataView.getUint8(offset);
+ }
+ Buffer.prototype.getUint16 = function(offset) {
+ return this.dataView.getUint16(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getUint32 = function(offset) {
+ return this.dataView.getUint32(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getUint64 = function(offset) {
+ var lo, hi;
+ if (kHostIsLittleEndian) {
+ lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+ hi = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+ } else {
+ hi = this.dataView.getUint32(offset, kHostIsLittleEndian);
+ lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+ }
+ return lo + hi * kHighWordMultiplier;
+ }
+
+ Buffer.prototype.getInt8 = function(offset) {
+ return this.dataView.getInt8(offset);
+ }
+ Buffer.prototype.getInt16 = function(offset) {
+ return this.dataView.getInt16(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getInt32 = function(offset) {
+ return this.dataView.getInt32(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getInt64 = function(offset) {
+ var lo, hi;
+ if (kHostIsLittleEndian) {
+ lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+ hi = this.dataView.getInt32(offset + 4, kHostIsLittleEndian);
+ } else {
+ hi = this.dataView.getInt32(offset, kHostIsLittleEndian);
+ lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+ }
+ return lo + hi * kHighWordMultiplier;
+ }
+
+ Buffer.prototype.getFloat32 = function(offset) {
+ return this.dataView.getFloat32(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getFloat64 = function(offset) {
+ return this.dataView.getFloat64(offset, kHostIsLittleEndian);
+ }
+
+ Buffer.prototype.setUint8 = function(offset, value) {
+ this.dataView.setUint8(offset, value);
+ }
+ Buffer.prototype.setUint16 = function(offset, value) {
+ this.dataView.setUint16(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setUint32 = function(offset, value) {
+ this.dataView.setUint32(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setUint64 = function(offset, value) {
+ var hi = (value / kHighWordMultiplier) | 0;
+ if (kHostIsLittleEndian) {
+ this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+ } else {
+ this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+ }
+ }
+
+ Buffer.prototype.setInt8 = function(offset, value) {
+ this.dataView.setInt8(offset, value);
+ }
+ Buffer.prototype.setInt16 = function(offset, value) {
+ this.dataView.setInt16(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setInt32 = function(offset, value) {
+ this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setInt64 = function(offset, value) {
+ var hi = Math.floor(value / kHighWordMultiplier);
+ if (kHostIsLittleEndian) {
+ this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+ } else {
+ this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+ }
+ }
+
+ Buffer.prototype.setFloat32 = function(offset, value) {
+ this.dataView.setFloat32(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setFloat64 = function(offset, value) {
+ this.dataView.setFloat64(offset, value, kHostIsLittleEndian);
+ }
+
+ var exports = {};
+ exports.Buffer = Buffer;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/codec.js b/mojo/public/js/bindings/codec.js
new file mode 100644
index 0000000..623cca7
--- /dev/null
+++ b/mojo/public/js/bindings/codec.js
@@ -0,0 +1,739 @@
+// 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("mojo/public/js/bindings/codec", [
+ "mojo/public/js/bindings/unicode",
+ "mojo/public/js/bindings/buffer"
+ ], function(unicode, buffer) {
+
+ var kErrorUnsigned = "Passing negative value to unsigned";
+
+ // Memory -------------------------------------------------------------------
+
+ var kAlignment = 8;
+
+ function align(size) {
+ return size + (kAlignment - (size % kAlignment)) % kAlignment;
+ }
+
+ function isAligned(offset) {
+ return offset >= 0 && (offset % kAlignment) === 0;
+ }
+
+ // Constants ----------------------------------------------------------------
+
+ var kArrayHeaderSize = 8;
+ var kStructHeaderSize = 8;
+ var kMessageHeaderSize = 16;
+ var kMessageWithRequestIDHeaderSize = 24;
+
+ var kStructHeaderNumBytesOffset = 0;
+ var kStructHeaderNumFieldsOffset = 4;
+
+ var kEncodedInvalidHandleValue = 0xFFFFFFFF;
+
+ // Decoder ------------------------------------------------------------------
+
+ function Decoder(buffer, handles, base) {
+ this.buffer = buffer;
+ this.handles = handles;
+ this.base = base;
+ this.next = base;
+ }
+
+ Decoder.prototype.skip = function(offset) {
+ this.next += offset;
+ };
+
+ Decoder.prototype.readInt8 = function() {
+ var result = this.buffer.getInt8(this.next);
+ this.next += 1;
+ return result;
+ };
+
+ Decoder.prototype.readUint8 = function() {
+ var result = this.buffer.getUint8(this.next);
+ this.next += 1;
+ return result;
+ };
+
+ Decoder.prototype.readInt16 = function() {
+ var result = this.buffer.getInt16(this.next);
+ this.next += 2;
+ return result;
+ };
+
+ Decoder.prototype.readUint16 = function() {
+ var result = this.buffer.getUint16(this.next);
+ this.next += 2;
+ return result;
+ };
+
+ Decoder.prototype.readInt32 = function() {
+ var result = this.buffer.getInt32(this.next);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readUint32 = function() {
+ var result = this.buffer.getUint32(this.next);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readInt64 = function() {
+ var result = this.buffer.getInt64(this.next);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.readUint64 = function() {
+ var result = this.buffer.getUint64(this.next);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.readFloat = function() {
+ var result = this.buffer.getFloat32(this.next);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readDouble = function() {
+ var result = this.buffer.getFloat64(this.next);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.decodePointer = function() {
+ // TODO(abarth): To correctly decode a pointer, we need to know the real
+ // base address of the array buffer.
+ var offsetPointer = this.next;
+ var offset = this.readUint64();
+ if (!offset)
+ return 0;
+ return offsetPointer + offset;
+ };
+
+ Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
+ return new Decoder(this.buffer, this.handles, pointer);
+ };
+
+ Decoder.prototype.decodeHandle = function() {
+ return this.handles[this.readUint32()];
+ };
+
+ Decoder.prototype.decodeString = function() {
+ var numberOfBytes = this.readUint32();
+ var numberOfElements = this.readUint32();
+ var base = this.next;
+ this.next += numberOfElements;
+ return unicode.decodeUtf8String(
+ new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
+ };
+
+ Decoder.prototype.decodeArray = function(cls) {
+ var numberOfBytes = this.readUint32();
+ var numberOfElements = this.readUint32();
+ var val = new Array(numberOfElements);
+ if (cls === PackedBool) {
+ var byte;
+ for (var i = 0; i < numberOfElements; ++i) {
+ if (i % 8 === 0)
+ byte = this.readUint8();
+ val[i] = (byte & (1 << i % 8)) ? true : false;
+ }
+ } else {
+ for (var i = 0; i < numberOfElements; ++i) {
+ val[i] = cls.decode(this);
+ }
+ }
+ return val;
+ };
+
+ Decoder.prototype.decodeStruct = function(cls) {
+ return cls.decode(this);
+ };
+
+ Decoder.prototype.decodeStructPointer = function(cls) {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return cls.decode(this.decodeAndCreateDecoder(pointer));
+ };
+
+ Decoder.prototype.decodeArrayPointer = function(cls) {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
+ };
+
+ Decoder.prototype.decodeStringPointer = function() {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return this.decodeAndCreateDecoder(pointer).decodeString();
+ };
+
+ // Encoder ------------------------------------------------------------------
+
+ function Encoder(buffer, handles, base) {
+ this.buffer = buffer;
+ this.handles = handles;
+ this.base = base;
+ this.next = base;
+ }
+
+ Encoder.prototype.skip = function(offset) {
+ this.next += offset;
+ };
+
+ Encoder.prototype.writeInt8 = function(val) {
+ this.buffer.setInt8(this.next, val);
+ this.next += 1;
+ };
+
+ Encoder.prototype.writeUint8 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint8(this.next, val);
+ this.next += 1;
+ };
+
+ Encoder.prototype.writeInt16 = function(val) {
+ this.buffer.setInt16(this.next, val);
+ this.next += 2;
+ };
+
+ Encoder.prototype.writeUint16 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint16(this.next, val);
+ this.next += 2;
+ };
+
+ Encoder.prototype.writeInt32 = function(val) {
+ this.buffer.setInt32(this.next, val);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeUint32 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint32(this.next, val);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeInt64 = function(val) {
+ this.buffer.setInt64(this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.writeUint64 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint64(this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.writeFloat = function(val) {
+ this.buffer.setFloat32(this.next, val);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeDouble = function(val) {
+ this.buffer.setFloat64(this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.encodePointer = function(pointer) {
+ if (!pointer)
+ return this.writeUint64(0);
+ // TODO(abarth): To correctly encode a pointer, we need to know the real
+ // base address of the array buffer.
+ var offset = pointer - this.next;
+ this.writeUint64(offset);
+ };
+
+ Encoder.prototype.createAndEncodeEncoder = function(size) {
+ var pointer = this.buffer.alloc(align(size));
+ this.encodePointer(pointer);
+ return new Encoder(this.buffer, this.handles, pointer);
+ };
+
+ Encoder.prototype.encodeHandle = function(handle) {
+ this.handles.push(handle);
+ this.writeUint32(this.handles.length - 1);
+ };
+
+ Encoder.prototype.encodeString = function(val) {
+ var base = this.next + kArrayHeaderSize;
+ var numberOfElements = unicode.encodeUtf8String(
+ val, new Uint8Array(this.buffer.arrayBuffer, base));
+ var numberOfBytes = kArrayHeaderSize + numberOfElements;
+ this.writeUint32(numberOfBytes);
+ this.writeUint32(numberOfElements);
+ this.next += numberOfElements;
+ };
+
+ Encoder.prototype.encodeArray =
+ function(cls, val, numberOfElements, encodedSize) {
+ if (numberOfElements === undefined)
+ numberOfElements = val.length;
+ if (encodedSize === undefined)
+ encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
+
+ this.writeUint32(encodedSize);
+ this.writeUint32(numberOfElements);
+
+ if (cls === PackedBool) {
+ var byte = 0;
+ for (i = 0; i < numberOfElements; ++i) {
+ if (val[i])
+ byte |= (1 << i % 8);
+ if (i % 8 === 7 || i == numberOfElements - 1) {
+ Uint8.encode(this, byte);
+ byte = 0;
+ }
+ }
+ } else {
+ for (var i = 0; i < numberOfElements; ++i)
+ cls.encode(this, val[i]);
+ }
+ };
+
+ Encoder.prototype.encodeStruct = function(cls, val) {
+ return cls.encode(this, val);
+ };
+
+ Encoder.prototype.encodeStructPointer = function(cls, val) {
+ if (val == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+ var encoder = this.createAndEncodeEncoder(cls.encodedSize);
+ cls.encode(encoder, val);
+ };
+
+ Encoder.prototype.encodeArrayPointer = function(cls, val) {
+ if (val == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+ var numberOfElements = val.length;
+ var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ?
+ Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeArray(cls, val, numberOfElements, encodedSize);
+ };
+
+ Encoder.prototype.encodeStringPointer = function(val) {
+ if (val == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+ var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeString(val);
+ };
+
+ // Message ------------------------------------------------------------------
+
+ var kMessageNameOffset = kStructHeaderSize;
+ var kMessageFlagsOffset = kMessageNameOffset + 4;
+ var kMessageRequestIDOffset = kMessageFlagsOffset + 4;
+
+ var kMessageExpectsResponse = 1 << 0;
+ var kMessageIsResponse = 1 << 1;
+
+ function Message(buffer, handles) {
+ this.buffer = buffer;
+ this.handles = handles;
+ }
+
+ Message.prototype.getHeaderNumBytes = function() {
+ return this.buffer.getUint32(kStructHeaderNumBytesOffset);
+ };
+
+ Message.prototype.getHeaderNumFields = function() {
+ return this.buffer.getUint32(kStructHeaderNumFieldsOffset);
+ };
+
+ Message.prototype.getName = function() {
+ return this.buffer.getUint32(kMessageNameOffset);
+ };
+
+ Message.prototype.getFlags = function() {
+ return this.buffer.getUint32(kMessageFlagsOffset);
+ };
+
+ Message.prototype.isResponse = function() {
+ return (this.getFlags() & kMessageIsResponse) != 0;
+ };
+
+ Message.prototype.expectsResponse = function() {
+ return (this.getFlags() & kMessageExpectsResponse) != 0;
+ };
+
+ Message.prototype.setRequestID = function(requestID) {
+ // TODO(darin): Verify that space was reserved for this field!
+ this.buffer.setUint64(kMessageRequestIDOffset, requestID);
+ };
+
+
+ // MessageBuilder -----------------------------------------------------------
+
+ function MessageBuilder(messageName, payloadSize) {
+ // Currently, we don't compute the payload size correctly ahead of time.
+ // Instead, we resize the buffer at the end.
+ var numberOfBytes = kMessageHeaderSize + payloadSize;
+ this.buffer = new buffer.Buffer(numberOfBytes);
+ this.handles = [];
+ var encoder = this.createEncoder(kMessageHeaderSize);
+ encoder.writeUint32(kMessageHeaderSize);
+ encoder.writeUint32(2); // num_fields.
+ encoder.writeUint32(messageName);
+ encoder.writeUint32(0); // flags.
+ }
+
+ MessageBuilder.prototype.createEncoder = function(size) {
+ var pointer = this.buffer.alloc(size);
+ return new Encoder(this.buffer, this.handles, pointer);
+ };
+
+ MessageBuilder.prototype.encodeStruct = function(cls, val) {
+ cls.encode(this.createEncoder(cls.encodedSize), val);
+ };
+
+ MessageBuilder.prototype.finish = function() {
+ // TODO(abarth): Rather than resizing the buffer at the end, we could
+ // compute the size we need ahead of time, like we do in C++.
+ this.buffer.trim();
+ var message = new Message(this.buffer, this.handles);
+ this.buffer = null;
+ this.handles = null;
+ this.encoder = null;
+ return message;
+ };
+
+ // MessageWithRequestIDBuilder -----------------------------------------------
+
+ function MessageWithRequestIDBuilder(messageName, payloadSize, flags,
+ requestID) {
+ // Currently, we don't compute the payload size correctly ahead of time.
+ // Instead, we resize the buffer at the end.
+ var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize;
+ this.buffer = new buffer.Buffer(numberOfBytes);
+ this.handles = [];
+ var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
+ encoder.writeUint32(kMessageWithRequestIDHeaderSize);
+ encoder.writeUint32(3); // num_fields.
+ encoder.writeUint32(messageName);
+ encoder.writeUint32(flags);
+ encoder.writeUint64(requestID);
+ }
+
+ MessageWithRequestIDBuilder.prototype =
+ Object.create(MessageBuilder.prototype);
+
+ MessageWithRequestIDBuilder.prototype.constructor =
+ MessageWithRequestIDBuilder;
+
+ // MessageReader ------------------------------------------------------------
+
+ function MessageReader(message) {
+ this.decoder = new Decoder(message.buffer, message.handles, 0);
+ var messageHeaderSize = this.decoder.readUint32();
+ this.payloadSize = message.buffer.byteLength - messageHeaderSize;
+ var numFields = this.decoder.readUint32();
+ this.messageName = this.decoder.readUint32();
+ this.flags = this.decoder.readUint32();
+ if (numFields >= 3)
+ this.requestID = this.decoder.readUint64();
+ this.decoder.skip(messageHeaderSize - this.decoder.next);
+ }
+
+ MessageReader.prototype.decodeStruct = function(cls) {
+ return cls.decode(this.decoder);
+ };
+
+ // Built-in types -----------------------------------------------------------
+
+ // This type is only used with ArrayOf(PackedBool).
+ function PackedBool() {
+ }
+
+ function Int8() {
+ }
+
+ Int8.encodedSize = 1;
+
+ Int8.decode = function(decoder) {
+ return decoder.readInt8();
+ };
+
+ Int8.encode = function(encoder, val) {
+ encoder.writeInt8(val);
+ };
+
+ Uint8.encode = function(encoder, val) {
+ encoder.writeUint8(val);
+ };
+
+ function Uint8() {
+ }
+
+ Uint8.encodedSize = 1;
+
+ Uint8.decode = function(decoder) {
+ return decoder.readUint8();
+ };
+
+ Uint8.encode = function(encoder, val) {
+ encoder.writeUint8(val);
+ };
+
+ function Int16() {
+ }
+
+ Int16.encodedSize = 2;
+
+ Int16.decode = function(decoder) {
+ return decoder.readInt16();
+ };
+
+ Int16.encode = function(encoder, val) {
+ encoder.writeInt16(val);
+ };
+
+ function Uint16() {
+ }
+
+ Uint16.encodedSize = 2;
+
+ Uint16.decode = function(decoder) {
+ return decoder.readUint16();
+ };
+
+ Uint16.encode = function(encoder, val) {
+ encoder.writeUint16(val);
+ };
+
+ function Int32() {
+ }
+
+ Int32.encodedSize = 4;
+
+ Int32.decode = function(decoder) {
+ return decoder.readInt32();
+ };
+
+ Int32.encode = function(encoder, val) {
+ encoder.writeInt32(val);
+ };
+
+ function Uint32() {
+ }
+
+ Uint32.encodedSize = 4;
+
+ Uint32.decode = function(decoder) {
+ return decoder.readUint32();
+ };
+
+ Uint32.encode = function(encoder, val) {
+ encoder.writeUint32(val);
+ };
+
+ function Int64() {
+ }
+
+ Int64.encodedSize = 8;
+
+ Int64.decode = function(decoder) {
+ return decoder.readInt64();
+ };
+
+ Int64.encode = function(encoder, val) {
+ encoder.writeInt64(val);
+ };
+
+ function Uint64() {
+ }
+
+ Uint64.encodedSize = 8;
+
+ Uint64.decode = function(decoder) {
+ return decoder.readUint64();
+ };
+
+ Uint64.encode = function(encoder, val) {
+ encoder.writeUint64(val);
+ };
+
+ function String() {
+ };
+
+ String.encodedSize = 8;
+
+ String.decode = function(decoder) {
+ return decoder.decodeStringPointer();
+ };
+
+ String.encode = function(encoder, val) {
+ encoder.encodeStringPointer(val);
+ };
+
+ function NullableString() {
+ }
+
+ NullableString.encodedSize = String.encodedSize;
+
+ NullableString.decode = String.decode;
+
+ NullableString.encode = String.encode;
+
+ function Float() {
+ }
+
+ Float.encodedSize = 4;
+
+ Float.decode = function(decoder) {
+ return decoder.readFloat();
+ };
+
+ Float.encode = function(encoder, val) {
+ encoder.writeFloat(val);
+ };
+
+ function Double() {
+ }
+
+ Double.encodedSize = 8;
+
+ Double.decode = function(decoder) {
+ return decoder.readDouble();
+ };
+
+ Double.encode = function(encoder, val) {
+ encoder.writeDouble(val);
+ };
+
+ function PointerTo(cls) {
+ this.cls = cls;
+ }
+
+ PointerTo.prototype.encodedSize = 8;
+
+ PointerTo.prototype.decode = function(decoder) {
+ var pointer = decoder.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
+ };
+
+ PointerTo.prototype.encode = function(encoder, val) {
+ if (!val) {
+ encoder.encodePointer(val);
+ return;
+ }
+ var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
+ this.cls.encode(objectEncoder, val);
+ };
+
+ function NullablePointerTo(cls) {
+ PointerTo.call(this, cls);
+ }
+
+ NullablePointerTo.prototype = Object.create(PointerTo.prototype);
+
+ function ArrayOf(cls) {
+ this.cls = cls;
+ }
+
+ ArrayOf.prototype.encodedSize = 8;
+
+ ArrayOf.prototype.decode = function(decoder) {
+ return decoder.decodeArrayPointer(this.cls);
+ };
+
+ ArrayOf.prototype.encode = function(encoder, val) {
+ encoder.encodeArrayPointer(this.cls, val);
+ };
+
+ function NullableArrayOf(cls) {
+ ArrayOf.call(this, cls);
+ }
+
+ NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
+
+ function Handle() {
+ }
+
+ Handle.encodedSize = 4;
+
+ Handle.decode = function(decoder) {
+ return decoder.decodeHandle();
+ };
+
+ Handle.encode = function(encoder, val) {
+ encoder.encodeHandle(val);
+ };
+
+ function NullableHandle() {
+ }
+
+ NullableHandle.encodedSize = Handle.encodedSize;
+
+ NullableHandle.decode = Handle.decode;
+
+ NullableHandle.encode = Handle.encode;
+
+ var exports = {};
+ exports.align = align;
+ exports.isAligned = isAligned;
+ exports.Message = Message;
+ exports.MessageBuilder = MessageBuilder;
+ exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
+ exports.MessageReader = MessageReader;
+ exports.kArrayHeaderSize = kArrayHeaderSize;
+ exports.kStructHeaderSize = kStructHeaderSize;
+ exports.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue;
+ exports.kMessageHeaderSize = kMessageHeaderSize;
+ exports.kMessageWithRequestIDHeaderSize = kMessageWithRequestIDHeaderSize;
+ exports.kMessageExpectsResponse = kMessageExpectsResponse;
+ exports.kMessageIsResponse = kMessageIsResponse;
+ exports.Int8 = Int8;
+ exports.Uint8 = Uint8;
+ exports.Int16 = Int16;
+ exports.Uint16 = Uint16;
+ exports.Int32 = Int32;
+ exports.Uint32 = Uint32;
+ exports.Int64 = Int64;
+ exports.Uint64 = Uint64;
+ exports.Float = Float;
+ exports.Double = Double;
+ exports.String = String;
+ exports.NullableString = NullableString;
+ exports.PointerTo = PointerTo;
+ exports.NullablePointerTo = NullablePointerTo;
+ exports.ArrayOf = ArrayOf;
+ exports.NullableArrayOf = NullableArrayOf;
+ exports.PackedBool = PackedBool;
+ exports.Handle = Handle;
+ exports.NullableHandle = NullableHandle;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/codec_unittests.js b/mojo/public/js/bindings/codec_unittests.js
new file mode 100644
index 0000000..0276db2
--- /dev/null
+++ b/mojo/public/js/bindings/codec_unittests.js
@@ -0,0 +1,258 @@
+// 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([
+ "gin/test/expect",
+ "mojo/public/js/bindings/codec",
+ "mojo/public/interfaces/bindings/tests/rect.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/interfaces/bindings/tests/test_structs.mojom",
+ ], function(expect, codec, rect, sample, structs) {
+ testBar();
+ testFoo();
+ testNamedRegion();
+ testTypes();
+ testAlign();
+ testUtf8();
+ this.result = "PASS";
+
+ function testBar() {
+ var bar = new sample.Bar();
+ bar.alpha = 1;
+ bar.beta = 2;
+ bar.gamma = 3;
+ bar.type = 0x08070605;
+ bar.extraProperty = "banana";
+
+ var messageName = 42;
+ var payloadSize = sample.Bar.encodedSize;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(sample.Bar, bar);
+
+ var message = builder.finish();
+
+ var expectedMemory = new Uint8Array([
+ 16, 0, 0, 0,
+ 2, 0, 0, 0,
+ 42, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 16, 0, 0, 0,
+ 4, 0, 0, 0,
+
+ 1, 2, 3, 0,
+ 5, 6, 7, 8,
+ ]);
+
+ var actualMemory = new Uint8Array(message.buffer.arrayBuffer);
+ expect(actualMemory).toEqual(expectedMemory);
+
+ var reader = new codec.MessageReader(message);
+
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+
+ var bar2 = reader.decodeStruct(sample.Bar);
+
+ expect(bar2.alpha).toBe(bar.alpha);
+ expect(bar2.beta).toBe(bar.beta);
+ expect(bar2.gamma).toBe(bar.gamma);
+ expect("extraProperty" in bar2).toBeFalsy();
+ }
+
+ function testFoo() {
+ var foo = new sample.Foo();
+ foo.x = 0x212B4D5;
+ foo.y = 0x16E93;
+ foo.a = 1;
+ foo.b = 0;
+ foo.c = 3; // This will get truncated to one bit.
+ foo.bar = new sample.Bar();
+ foo.bar.alpha = 91;
+ foo.bar.beta = 82;
+ foo.bar.gamma = 73;
+ foo.data = [
+ 4, 5, 6, 7, 8,
+ ];
+ foo.extra_bars = [
+ new sample.Bar(), new sample.Bar(), new sample.Bar(),
+ ];
+ for (var i = 0; i < foo.extra_bars.length; ++i) {
+ foo.extra_bars[i].alpha = 1 * i;
+ foo.extra_bars[i].beta = 2 * i;
+ foo.extra_bars[i].gamma = 3 * i;
+ }
+ foo.name = "I am a banana";
+ // This is supposed to be a handle, but we fake it with an integer.
+ foo.source = 23423782;
+ foo.array_of_array_of_bools = [
+ [true], [false, true]
+ ];
+ foo.array_of_bools = [
+ true, false, true, false, true, false, true, true
+ ];
+
+
+ var messageName = 31;
+ var payloadSize = 304;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(sample.Foo, foo);
+
+ var message = builder.finish();
+
+ var expectedMemory = new Uint8Array([
+ /* 0: */ 16, 0, 0, 0, 2, 0, 0, 0,
+ /* 8: */ 31, 0, 0, 0, 0, 0, 0, 0,
+ /* 16: */ 96, 0, 0, 0, 15, 0, 0, 0,
+ /* 24: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01, 0,
+ /* 32: */ 5, 0, 0, 0, 0, 0, 0, 0,
+ /* 40: */ 72, 0, 0, 0, 0, 0, 0, 0,
+ ]);
+ // TODO(abarth): Test more of the message's raw memory.
+ var actualMemory = new Uint8Array(message.buffer.arrayBuffer,
+ 0, expectedMemory.length);
+ expect(actualMemory).toEqual(expectedMemory);
+
+ var expectedHandles = [
+ 23423782,
+ ];
+
+ expect(message.handles).toEqual(expectedHandles);
+
+ var reader = new codec.MessageReader(message);
+
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+
+ var foo2 = reader.decodeStruct(sample.Foo);
+
+ expect(foo2.x).toBe(foo.x);
+ expect(foo2.y).toBe(foo.y);
+
+ expect(foo2.a).toBe(foo.a & 1 ? true : false);
+ expect(foo2.b).toBe(foo.b & 1 ? true : false);
+ expect(foo2.c).toBe(foo.c & 1 ? true : false);
+
+ expect(foo2.bar).toEqual(foo.bar);
+ expect(foo2.data).toEqual(foo.data);
+
+ expect(foo2.extra_bars).toEqual(foo.extra_bars);
+ expect(foo2.name).toBe(foo.name);
+ expect(foo2.source).toEqual(foo.source);
+
+ expect(foo2.array_of_bools).toEqual(foo.array_of_bools);
+ }
+
+ function createRect(x, y, width, height) {
+ var r = new rect.Rect();
+ r.x = x;
+ r.y = y;
+ r.width = width;
+ r.height = height;
+ return r;
+ }
+
+ // Verify that the references to the imported Rect type in test_structs.mojom
+ // are generated correctly.
+ function testNamedRegion() {
+ var r = new structs.NamedRegion();
+ r.name = "rectangle";
+ r.rects = new Array(createRect(1, 2, 3, 4), createRect(10, 20, 30, 40));
+
+ var builder = new codec.MessageBuilder(1, structs.NamedRegion.encodedSize);
+ builder.encodeStruct(structs.NamedRegion, r);
+ var reader = new codec.MessageReader(builder.finish());
+ var result = reader.decodeStruct(structs.NamedRegion);
+
+ expect(result.name).toEqual("rectangle");
+ expect(result.rects[0]).toEqual(createRect(1, 2, 3, 4));
+ expect(result.rects[1]).toEqual(createRect(10, 20, 30, 40));
+ }
+
+ function testTypes() {
+ function encodeDecode(cls, input, expectedResult, encodedSize) {
+ var messageName = 42;
+ var payloadSize = encodedSize || cls.encodedSize;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(cls, input)
+ var message = builder.finish();
+
+ var reader = new codec.MessageReader(message);
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+ var result = reader.decodeStruct(cls);
+ expect(result).toEqual(expectedResult);
+ }
+ encodeDecode(codec.String, "banana", "banana", 24);
+ encodeDecode(codec.NullableString, null, null, 8);
+ encodeDecode(codec.Int8, -1, -1);
+ encodeDecode(codec.Int8, 0xff, -1);
+ encodeDecode(codec.Int16, -1, -1);
+ encodeDecode(codec.Int16, 0xff, 0xff);
+ encodeDecode(codec.Int16, 0xffff, -1);
+ encodeDecode(codec.Int32, -1, -1);
+ encodeDecode(codec.Int32, 0xffff, 0xffff);
+ encodeDecode(codec.Int32, 0xffffffff, -1);
+ encodeDecode(codec.Float, 1.0, 1.0);
+ encodeDecode(codec.Double, 1.0, 1.0);
+ }
+
+ function testAlign() {
+ var aligned = [
+ 0, // 0
+ 8, // 1
+ 8, // 2
+ 8, // 3
+ 8, // 4
+ 8, // 5
+ 8, // 6
+ 8, // 7
+ 8, // 8
+ 16, // 9
+ 16, // 10
+ 16, // 11
+ 16, // 12
+ 16, // 13
+ 16, // 14
+ 16, // 15
+ 16, // 16
+ 24, // 17
+ 24, // 18
+ 24, // 19
+ 24, // 20
+ ];
+ for (var i = 0; i < aligned.length; ++i)
+ expect(codec.align(i)).toBe(aligned[i]);
+ }
+
+ function testUtf8() {
+ var str = "B\u03ba\u1f79"; // some UCS-2 codepoints
+ var messageName = 42;
+ var payloadSize = 24;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ var encoder = builder.createEncoder(8);
+ encoder.encodeStringPointer(str);
+ var message = builder.finish();
+ var expectedMemory = new Uint8Array([
+ /* 0: */ 16, 0, 0, 0, 2, 0, 0, 0,
+ /* 8: */ 42, 0, 0, 0, 0, 0, 0, 0,
+ /* 16: */ 8, 0, 0, 0, 0, 0, 0, 0,
+ /* 24: */ 14, 0, 0, 0, 6, 0, 0, 0,
+ /* 32: */ 0x42, 0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0, 0,
+ ]);
+ var actualMemory = new Uint8Array(message.buffer.arrayBuffer);
+ expect(actualMemory.length).toEqual(expectedMemory.length);
+ expect(actualMemory).toEqual(expectedMemory);
+
+ var reader = new codec.MessageReader(message);
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+ var str2 = reader.decoder.decodeStringPointer();
+ expect(str2).toEqual(str);
+ }
+});
diff --git a/mojo/public/js/bindings/connection.js b/mojo/public/js/bindings/connection.js
new file mode 100644
index 0000000..31cf2aa
--- /dev/null
+++ b/mojo/public/js/bindings/connection.js
@@ -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.
+
+define("mojo/public/js/bindings/connection", [
+ "mojo/public/js/bindings/connector",
+ "mojo/public/js/bindings/router",
+], function(connector, router) {
+
+ function Connection(
+ handle, localFactory, remoteFactory, routerFactory, connectorFactory) {
+ if (routerFactory === undefined)
+ routerFactory = router.Router;
+ this.router_ = new routerFactory(handle, connectorFactory);
+ this.remote = new remoteFactory(this.router_);
+ this.local = new localFactory(this.remote);
+ this.router_.setIncomingReceiver(this.local);
+
+ var validateRequest = localFactory.prototype.validator;
+ var validateResponse = remoteFactory.prototype.validator;
+ var payloadValidators = [];
+ if (validateRequest)
+ payloadValidators.push(validateRequest);
+ if (validateResponse)
+ payloadValidators.push(validateResponse);
+ this.router_.setPayloadValidators(payloadValidators);
+ }
+
+ Connection.prototype.close = function() {
+ this.router_.close();
+ this.router_ = null;
+ this.local = null;
+ this.remote = null;
+ };
+
+ Connection.prototype.encounteredError = function() {
+ return this.router_.encounteredError();
+ };
+
+ // The TestConnection subclass is only intended to be used in unit tests.
+
+ function TestConnection(handle, localFactory, remoteFactory) {
+ Connection.call(this,
+ handle,
+ localFactory,
+ remoteFactory,
+ router.TestRouter,
+ connector.TestConnector);
+ }
+
+ TestConnection.prototype = Object.create(Connection.prototype);
+
+ var exports = {};
+ exports.Connection = Connection;
+ exports.TestConnection = TestConnection;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/connector.js b/mojo/public/js/bindings/connector.js
new file mode 100644
index 0000000..495896d
--- /dev/null
+++ b/mojo/public/js/bindings/connector.js
@@ -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.
+
+define("mojo/public/js/bindings/connector", [
+ "mojo/public/js/bindings/buffer",
+ "mojo/public/js/bindings/codec",
+ "mojo/public/js/bindings/core",
+ "mojo/public/js/bindings/support",
+], function(buffer, codec, core, support) {
+
+ function Connector(handle) {
+ this.handle_ = handle;
+ this.dropWrites_ = false;
+ this.error_ = false;
+ this.incomingReceiver_ = null;
+ this.readWaitCookie_ = null;
+ this.errorHandler_ = null;
+
+ this.waitToReadMore_();
+ }
+
+ Connector.prototype.close = function() {
+ if (this.readWaitCookie_) {
+ support.cancelWait(this.readWaitCookie_);
+ this.readWaitCookie_ = null;
+ }
+ if (this.handle_ != null) {
+ core.close(this.handle_);
+ this.handle_ = null;
+ }
+ };
+
+ Connector.prototype.accept = function(message) {
+ if (this.error_)
+ return false;
+
+ if (this.dropWrites_)
+ return true;
+
+ var result = core.writeMessage(this.handle_,
+ new Uint8Array(message.buffer.arrayBuffer),
+ message.handles,
+ core.WRITE_MESSAGE_FLAG_NONE);
+ switch (result) {
+ case core.RESULT_OK:
+ // The handles were successfully transferred, so we don't own them
+ // anymore.
+ message.handles = [];
+ break;
+ case core.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.
+ this.dropWrites_ = true;
+ break;
+ default:
+ // This particular write was rejected, presumably because of bad input.
+ // The pipe is not necessarily in a bad state.
+ return false;
+ }
+ return true;
+ };
+
+ Connector.prototype.setIncomingReceiver = function(receiver) {
+ this.incomingReceiver_ = receiver;
+ };
+
+ Connector.prototype.setErrorHandler = function(handler) {
+ this.errorHandler_ = handler;
+ };
+
+ Connector.prototype.encounteredError = function() {
+ return this.error_;
+ };
+
+ Connector.prototype.waitToReadMore_ = function() {
+ this.readWaitCookie_ = support.asyncWait(this.handle_,
+ core.HANDLE_SIGNAL_READABLE,
+ this.readMore_.bind(this));
+ };
+
+ Connector.prototype.readMore_ = function(result) {
+ for (;;) {
+ var read = core.readMessage(this.handle_,
+ core.READ_MESSAGE_FLAG_NONE);
+ if (read.result == core.RESULT_SHOULD_WAIT) {
+ this.waitToReadMore_();
+ return;
+ }
+ if (read.result != core.RESULT_OK) {
+ this.error_ = true;
+ if (this.errorHandler_)
+ this.errorHandler_.onError(read.result);
+ return;
+ }
+ var messageBuffer = new buffer.Buffer(read.buffer);
+ var message = new codec.Message(messageBuffer, read.handles);
+ if (this.incomingReceiver_) {
+ this.incomingReceiver_.accept(message);
+ }
+ }
+ };
+
+ // The TestConnector subclass is only intended to be used in unit tests. It
+ // enables delivering a message to the pipe's handle without an async wait.
+
+ function TestConnector(handle) {
+ Connector.call(this, handle);
+ }
+
+ TestConnector.prototype = Object.create(Connector.prototype);
+
+ TestConnector.prototype.waitToReadMore_ = function() {
+ };
+
+ TestConnector.prototype.deliverMessage = function() {
+ this.readMore_(core.RESULT_OK);
+ }
+
+ var exports = {};
+ exports.Connector = Connector;
+ exports.TestConnector = TestConnector;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/constants.cc b/mojo/public/js/bindings/constants.cc
new file mode 100644
index 0000000..547279d
--- /dev/null
+++ b/mojo/public/js/bindings/constants.cc
@@ -0,0 +1,17 @@
+// 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/js/bindings/constants.h"
+
+namespace mojo {
+
+const char kBufferModuleName[] = "mojo/public/js/bindings/buffer";
+const char kCodecModuleName[] = "mojo/public/js/bindings/codec";
+const char kConnectionModuleName[] = "mojo/public/js/bindings/connection";
+const char kConnectorModuleName[] = "mojo/public/js/bindings/connector";
+const char kUnicodeModuleName[] = "mojo/public/js/bindings/unicode";
+const char kRouterModuleName[] = "mojo/public/js/bindings/router";
+const char kValidatorModuleName[] = "mojo/public/js/bindings/validator";
+
+} // namespace mojo
diff --git a/mojo/public/js/bindings/constants.h b/mojo/public/js/bindings/constants.h
new file mode 100644
index 0000000..9927c8e
--- /dev/null
+++ b/mojo/public/js/bindings/constants.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_JS_BINDINGS_CONSTANTS_H_
+#define MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
+
+namespace mojo {
+
+// JavaScript module names:
+extern const char kBufferModuleName[];
+extern const char kCodecModuleName[];
+extern const char kConnectionModuleName[];
+extern const char kConnectorModuleName[];
+extern const char kUnicodeModuleName[];
+extern const char kRouterModuleName[];
+extern const char kValidatorModuleName[];
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
diff --git a/mojo/public/js/bindings/core.js b/mojo/public/js/bindings/core.js
new file mode 100644
index 0000000..95d49a5
--- /dev/null
+++ b/mojo/public/js/bindings/core.js
@@ -0,0 +1,229 @@
+// 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.
+
+// Module "mojo/public/js/bindings/core"
+//
+// Note: This file is for documentation purposes only. The code here is not
+// actually executed. The real module is implemented natively in Mojo.
+//
+// This module provides the JavaScript bindings for mojo/public/c/system/core.h.
+// Refer to that file for more detailed documentation for equivalent methods.
+
+while (1);
+
+/**
+ * MojoHandle: An opaque handles to a Mojo object (e.g. a message pipe).
+ */
+var kInvalidHandle;
+
+/**
+ * MojoResult {number}: Result codes for Mojo operations.
+ * See core.h for more information.
+ */
+var RESULT_OK;
+var RESULT_CANCELLED;
+var RESULT_UNKNOWN;
+var RESULT_INVALID_ARGUMENT;
+var RESULT_DEADLINE_EXCEEDED;
+var RESULT_NOT_FOUND;
+var RESULT_ALREADY_EXISTS;
+var RESULT_PERMISSION_DENIED;
+var RESULT_RESOURCE_EXHAUSTED;
+var RESULT_FAILED_PRECONDITION;
+var RESULT_ABORTED;
+var RESULT_OUT_OF_RANGE;
+var RESULT_UNIMPLEMENTED;
+var RESULT_INTERNAL;
+var RESULT_UNAVAILABLE;
+var RESULT_DATA_LOSS;
+var RESULT_BUSY;
+var RESULT_SHOULD_WAIT;
+
+/**
+ * MojoDeadline {number}: Used to specify deadlines (timeouts), in microseconds.
+ * See core.h for more information.
+ */
+var DEADLINE_INDEFINITE;
+
+/**
+ * MojoHandleSignals: Used to specify signals that can be waited on for a handle
+ *(and which can be triggered), e.g., the ability to read or write to
+ * the handle.
+ * See core.h for more information.
+ */
+var HANDLE_SIGNAL_NONE;
+var HANDLE_SIGNAL_READABLE;
+var HANDLE_SIGNAL_WRITABLE;
+
+/**
+ * MojoCreateDataMessageOptions: Used to specify creation parameters for a data
+ * pipe to |createDataMessage()|.
+ * See core.h for more information.
+ */
+dictionary MojoCreateDataMessageOptions {
+ MojoCreateDataMessageOptionsFlags flags; // See below.
+};
+
+// MojoCreateDataMessageOptionsFlags
+var CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE;
+
+/*
+ * MojoWriteMessageFlags: Used to specify different modes to |writeMessage()|.
+ * See core.h for more information.
+ */
+var WRITE_MESSAGE_FLAG_NONE;
+
+/**
+ * MojoReadMessageFlags: Used to specify different modes to |readMessage()|.
+ * See core.h for more information.
+ */
+var READ_MESSAGE_FLAG_NONE;
+var READ_MESSAGE_FLAG_MAY_DISCARD;
+
+/**
+ * MojoCreateDataPipeOptions: Used to specify creation parameters for a data
+ * pipe to |createDataPipe()|.
+ * See core.h for more information.
+ */
+dictionary MojoCreateDataPipeOptions {
+ MojoCreateDataPipeOptionsFlags flags; // See below.
+ int32 elementNumBytes; // The size of an element, in bytes.
+ int32 capacityNumBytes; // The capacity of the data pipe, in bytes.
+};
+
+// MojoCreateDataPipeOptionsFlags
+var CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+var CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD;
+
+/*
+ * MojoWriteDataFlags: Used to specify different modes to |writeData()|.
+ * See core.h for more information.
+ */
+var WRITE_DATA_FLAG_NONE;
+var WRITE_DATA_FLAG_ALL_OR_NONE;
+
+/**
+ * MojoReadDataFlags: Used to specify different modes to |readData()|.
+ * See core.h for more information.
+ */
+var READ_DATA_FLAG_NONE;
+var READ_DATA_FLAG_ALL_OR_NONE;
+var READ_DATA_FLAG_DISCARD;
+var READ_DATA_FLAG_QUERY;
+
+/**
+ * Closes the given |handle|. See MojoClose for more info.
+ * @param {MojoHandle} Handle to close.
+ * @return {MojoResult} Result code.
+ */
+function close(handle) { [native code] }
+
+/**
+ * Waits on the given handle until a signal indicated by |signals| is
+ * satisfied or until |deadline| is passed. See MojoWait for more information.
+ *
+ * @param {MojoHandle} handle Handle to wait on.
+ * @param {MojoHandleSignals} signals Specifies the condition to wait for.
+ * @param {MojoDeadline} deadline Stops waiting if this is reached.
+ * @return {MojoResult} Result code.
+ */
+function wait(handle, signals, deadline) { [native code] }
+
+/**
+ * Waits on |handles[0]|, ..., |handles[handles.length-1]| for at least one of
+ * them to satisfy the state indicated by |flags[0]|, ...,
+ * |flags[handles.length-1]|, respectively, or until |deadline| has passed.
+ * See MojoWaitMany for more information.
+ *
+ * @param {Array.MojoHandle} handles Handles to wait on.
+ * @param {Array.MojoHandleSignals} signals Specifies the condition to wait for,
+ * for each corresponding handle. Must be the same length as |handles|.
+ * @param {MojoDeadline} deadline Stops waiting if this is reached.
+ * @return {MojoResult} Result code.
+ */
+function waitMany(handles, signals, deadline) { [native code] }
+
+/**
+ * Creates a message pipe. This function always succeeds.
+ * See MojoCreateMessagePipe for more information on message pipes.
+ *
+ * @param {MojoCreateMessagePipeOptions} optionsDict Options to control the
+ * message pipe parameters. May be null.
+ * @return {MessagePipe} An object of the form {
+ * handle0,
+ * handle1,
+ * }
+ * where |handle0| and |handle1| are MojoHandles to each end of the channel.
+ */
+function createMessagePipe(optionsDict) { [native code] }
+
+/**
+ * Writes a message to the message pipe endpoint given by |handle|. See
+ * MojoWriteMessage for more information, including return codes.
+ *
+ * @param {MojoHandle} handle The endpoint to write to.
+ * @param {ArrayBufferView} buffer The message data. May be empty.
+ * @param {Array.MojoHandle} handlesArray Any handles to attach. Handles are
+ * transferred on success and will no longer be valid. May be empty.
+ * @param {MojoWriteMessageFlags} flags Flags.
+ * @return {MojoResult} Result code.
+ */
+function writeMessage(handle, buffer, handlesArray, flags) { [native code] }
+
+/**
+ * Reads a message from the message pipe endpoint given by |handle|. See
+ * MojoReadMessage for more information, including return codes.
+ *
+ * @param {MojoHandle} handle The endpoint to read from.
+ * @param {MojoReadMessageFlags} flags Flags.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * buffer, // An ArrayBufferView of the message data (only on success).
+ * handles // An array of MojoHandles transferred, if any.
+ * }
+ */
+function readMessage(handle, flags) { [native code] }
+
+/**
+ * Creates a data pipe, which is a unidirectional communication channel for
+ * unframed data, with the given options. See MojoCreateDataPipe for more
+ * more information, including return codes.
+ *
+ * @param {MojoCreateDataPipeOptions} optionsDict Options to control the data
+ * pipe parameters. May be null.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * producerHandle, // MojoHandle to use with writeData (only on success).
+ * consumerHandle, // MojoHandle to use with readData (only on success).
+ * }
+ */
+function createDataPipe(optionsDict) { [native code] }
+
+/**
+ * Writes the given data to the data pipe producer given by |handle|. See
+ * MojoWriteData for more information, including return codes.
+ *
+ * @param {MojoHandle} handle A producerHandle returned by createDataPipe.
+ * @param {ArrayBufferView} buffer The data to write.
+ * @param {MojoWriteDataFlags} flags Flags.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * numBytes, // The number of bytes written.
+ * }
+ */
+function writeData(handle, buffer, flags) { [native code] }
+
+/**
+ * Reads data from the data pipe consumer given by |handle|. May also
+ * be used to discard data. See MojoReadData for more information, including
+ * return codes.
+ *
+ * @param {MojoHandle} handle A consumerHandle returned by createDataPipe.
+ * @param {MojoReadDataFlags} flags Flags.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * buffer, // An ArrayBufferView of the data read (only on success).
+ * }
+ */
+function readData(handle, flags) { [native code] }
diff --git a/mojo/public/js/bindings/core_unittests.js b/mojo/public/js/bindings/core_unittests.js
new file mode 100644
index 0000000..b115cc0
--- /dev/null
+++ b/mojo/public/js/bindings/core_unittests.js
@@ -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.
+
+define([
+ "gin/test/expect",
+ "mojo/public/js/bindings/core",
+ "gc",
+ ], function(expect, core, gc) {
+ runWithMessagePipe(testNop);
+ runWithMessagePipe(testReadAndWriteMessage);
+ runWithMessagePipeWithOptions(testNop);
+ runWithMessagePipeWithOptions(testReadAndWriteMessage);
+ runWithDataPipe(testNop);
+ runWithDataPipe(testReadAndWriteDataPipe);
+ runWithDataPipeWithOptions(testNop);
+ runWithDataPipeWithOptions(testReadAndWriteDataPipe);
+ gc.collectGarbage(); // should not crash
+ this.result = "PASS";
+
+ function runWithMessagePipe(test) {
+ var pipe = core.createMessagePipe();
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ test(pipe);
+
+ expect(core.close(pipe.handle0)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.handle1)).toBe(core.RESULT_OK);
+ }
+
+ function runWithMessagePipeWithOptions(test) {
+ var pipe = core.createMessagePipe({
+ flags: core.CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+ });
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ test(pipe);
+
+ expect(core.close(pipe.handle0)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.handle1)).toBe(core.RESULT_OK);
+ }
+
+ function runWithDataPipe(test) {
+ var pipe = core.createDataPipe();
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ test(pipe);
+
+ expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK);
+ }
+
+ function runWithDataPipeWithOptions(test) {
+ var pipe = core.createDataPipe({
+ flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ elementNumBytes: 1,
+ capacityNumBytes: 64
+ });
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ test(pipe);
+
+ expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK);
+ }
+
+ function testNop(pipe) {
+ }
+
+ function testReadAndWriteMessage(pipe) {
+ var senderData = new Uint8Array(42);
+ for (var i = 0; i < senderData.length; ++i) {
+ senderData[i] = i * i;
+ }
+
+ var result = core.writeMessage(
+ pipe.handle0, senderData, [],
+ core.WRITE_MESSAGE_FLAG_NONE);
+
+ expect(result).toBe(core.RESULT_OK);
+
+ var read = core.readMessage(
+ pipe.handle1, core.READ_MESSAGE_FLAG_NONE);
+
+ expect(read.result).toBe(core.RESULT_OK);
+ expect(read.buffer.byteLength).toBe(42);
+ expect(read.handles.length).toBe(0);
+
+ var memory = new Uint8Array(read.buffer);
+ for (var i = 0; i < memory.length; ++i)
+ expect(memory[i]).toBe((i * i) & 0xFF);
+ }
+
+ function testReadAndWriteDataPipe(pipe) {
+ var senderData = new Uint8Array(42);
+ for (var i = 0; i < senderData.length; ++i) {
+ senderData[i] = i * i;
+ }
+
+ var write = core.writeData(
+ pipe.producerHandle, senderData,
+ core.WRITE_DATA_FLAG_ALL_OR_NONE);
+
+ expect(write.result).toBe(core.RESULT_OK);
+ expect(write.numBytes).toBe(42);
+
+ var read = core.readData(
+ pipe.consumerHandle, core.READ_DATA_FLAG_ALL_OR_NONE);
+
+ expect(read.result).toBe(core.RESULT_OK);
+ expect(read.buffer.byteLength).toBe(42);
+
+ var memory = new Uint8Array(read.buffer);
+ for (var i = 0; i < memory.length; ++i)
+ expect(memory[i]).toBe((i * i) & 0xFF);
+ }
+
+});
diff --git a/mojo/public/js/bindings/router.js b/mojo/public/js/bindings/router.js
new file mode 100644
index 0000000..3682392
--- /dev/null
+++ b/mojo/public/js/bindings/router.js
@@ -0,0 +1,135 @@
+// 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("mojo/public/js/bindings/router", [
+ "mojo/public/js/bindings/codec",
+ "mojo/public/js/bindings/connector",
+ "mojo/public/js/bindings/validator",
+], function(codec, connector, validator) {
+
+ function Router(handle, connectorFactory) {
+ if (connectorFactory === undefined)
+ connectorFactory = connector.Connector;
+ this.connector_ = new connectorFactory(handle);
+ this.incomingReceiver_ = null;
+ this.nextRequestID_ = 0;
+ this.responders_ = {};
+ this.payloadValidators_ = [];
+
+ this.connector_.setIncomingReceiver({
+ accept: this.handleIncomingMessage_.bind(this),
+ });
+ this.connector_.setErrorHandler({
+ onError: this.handleConnectionError_.bind(this),
+ });
+ }
+
+ Router.prototype.close = function() {
+ this.responders_ = {}; // Drop any responders.
+ this.connector_.close();
+ };
+
+ Router.prototype.accept = function(message) {
+ this.connector_.accept(message);
+ };
+
+ Router.prototype.reject = function(message) {
+ // TODO(mpcomplete): no way to trasmit errors over a Connection.
+ };
+
+ Router.prototype.acceptWithResponder = function(message, responder) {
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ var requestID = this.nextRequestID_++;
+ if (requestID == 0)
+ requestID = this.nextRequestID_++;
+
+ message.setRequestID(requestID);
+ var result = this.connector_.accept(message);
+
+ this.responders_[requestID] = responder;
+
+ // TODO(mpcomplete): accept should return a Promise too, maybe?
+ if (result)
+ return Promise.resolve();
+ return Promise.reject(Error("Connection error"));
+ };
+
+ Router.prototype.setIncomingReceiver = function(receiver) {
+ this.incomingReceiver_ = receiver;
+ };
+
+ Router.prototype.setPayloadValidators = function(payloadValidators) {
+ this.payloadValidators_ = payloadValidators;
+ };
+
+ Router.prototype.encounteredError = function() {
+ return this.connector_.encounteredError();
+ };
+
+ Router.prototype.handleIncomingMessage_ = function(message) {
+ var noError = validator.validationError.NONE;
+ var messageValidator = new validator.Validator(message);
+ var err = messageValidator.validateMessageHeader();
+ for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
+ err = this.payloadValidators_[i](messageValidator);
+
+ if (err == noError)
+ this.handleValidIncomingMessage_(message);
+ else
+ this.handleInvalidIncomingMessage_(message, err);
+ };
+
+ Router.prototype.handleValidIncomingMessage_ = function(message) {
+ if (message.expectsResponse()) {
+ if (this.incomingReceiver_) {
+ this.incomingReceiver_.acceptWithResponder(message, this);
+ } else {
+ // If we receive a request expecting a response when the client is not
+ // listening, then we have no choice but to tear down the pipe.
+ this.close();
+ }
+ } else if (message.isResponse()) {
+ var reader = new codec.MessageReader(message);
+ var requestID = reader.requestID;
+ var responder = this.responders_[requestID];
+ delete this.responders_[requestID];
+ responder.accept(message);
+ } else {
+ if (this.incomingReceiver_)
+ this.incomingReceiver_.accept(message);
+ }
+ }
+
+ Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
+ this.close();
+ }
+
+ Router.prototype.handleConnectionError_ = function(result) {
+ for (var each in this.responders_)
+ this.responders_[each].reject(result);
+ this.close();
+ };
+
+ // The TestRouter subclass is only intended to be used in unit tests.
+ // It defeats valid message handling and delgates invalid message handling.
+
+ function TestRouter(handle, connectorFactory) {
+ Router.call(this, handle, connectorFactory);
+ }
+
+ TestRouter.prototype = Object.create(Router.prototype);
+
+ TestRouter.prototype.handleValidIncomingMessage_ = function() {
+ };
+
+ TestRouter.prototype.handleInvalidIncomingMessage_ =
+ function(message, error) {
+ this.validationErrorHandler(error);
+ };
+
+ var exports = {};
+ exports.Router = Router;
+ exports.TestRouter = TestRouter;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/struct_unittests.js b/mojo/public/js/bindings/struct_unittests.js
new file mode 100644
index 0000000..b9948a9
--- /dev/null
+++ b/mojo/public/js/bindings/struct_unittests.js
@@ -0,0 +1,79 @@
+// 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([
+ "gin/test/expect",
+ "mojo/public/interfaces/bindings/tests/rect.mojom",
+ "mojo/public/interfaces/bindings/tests/test_structs.mojom"
+], function(expect,
+ rect,
+ testStructs) {
+
+ function testConstructors() {
+ var r = new rect.Rect();
+ expect(r).toEqual(new rect.Rect({x:0, y:0, width:0, height:0}));
+ expect(r).toEqual(new rect.Rect({foo:100, bar:200}));
+
+ r.x = 10;
+ r.y = 20;
+ r.width = 30;
+ r.height = 40;
+ var rp = new testStructs.RectPair({first: r, second: r});
+ expect(rp.first).toEqual(r);
+ expect(rp.second).toEqual(r);
+
+ expect(new testStructs.RectPair({second: r}).first).toBeNull();
+
+ var nr = new testStructs.NamedRegion();
+ expect(nr.name).toBeNull();
+ expect(nr.rects).toBeNull();
+ expect(nr).toEqual(new testStructs.NamedRegion({}));
+
+ nr.name = "foo";
+ nr.rects = [r, r, r];
+ expect(nr).toEqual(new testStructs.NamedRegion({
+ name: "foo",
+ rects: [r, r, r],
+ }));
+
+ var e = new testStructs.EmptyStruct();
+ expect(e).toEqual(new testStructs.EmptyStruct({foo:123}));
+ }
+
+ function testNoDefaultFieldValues() {
+ var s = new testStructs.NoDefaultFieldValues();
+ expect(s.f0).toEqual(false);
+
+ // f1 - f10, number type fields
+ for (var i = 1; i <= 10; i++)
+ expect(s["f" + i]).toEqual(0);
+
+ // f11,12 strings, f13-22 handles, f23-f26 arrays, f27,28 structs
+ for (var i = 11; i <= 28; i++)
+ expect(s["f" + i]).toBeNull();
+ }
+
+ function testDefaultFieldValues() {
+ var s = new testStructs.DefaultFieldValues();
+ expect(s.f0).toEqual(true);
+
+ // f1 - f12, number type fields
+ for (var i = 1; i <= 12; i++)
+ expect(s["f" + i]).toEqual(100);
+
+ // f13,14 "foo"
+ for (var i = 13; i <= 14; i++)
+ expect(s["f" + i]).toEqual("foo");
+
+ // f15,16 a default instance of Rect
+ var r = new rect.Rect();
+ expect(s.f15).toEqual(r);
+ expect(s.f16).toEqual(r);
+ }
+
+ testConstructors();
+ testNoDefaultFieldValues();
+ testDefaultFieldValues();
+ this.result = "PASS";
+});
\ No newline at end of file
diff --git a/mojo/public/js/bindings/support.js b/mojo/public/js/bindings/support.js
new file mode 100644
index 0000000..58df6bd
--- /dev/null
+++ b/mojo/public/js/bindings/support.js
@@ -0,0 +1,30 @@
+// 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.
+
+// Module "mojo/public/js/bindings/support"
+//
+// Note: This file is for documentation purposes only. The code here is not
+// actually executed. The real module is implemented natively in Mojo.
+
+while (1);
+
+/*
+ * Waits on the given handle until the state indicated by |signals| is
+ * satisfied.
+ *
+ * @param {MojoHandle} handle The handle to wait on.
+ * @param {MojoHandleSignals} signals Specifies the condition to wait for.
+ * @param {function (mojoResult)} callback Called with the result the wait is
+ * complete. See MojoWait for possible result codes.
+ *
+ * @return {MojoWaitId} A waitId that can be passed to cancelWait to cancel the
+ * wait.
+ */
+function asyncWait(handle, signals, callback) { [native code] }
+
+/*
+ * Cancels the asyncWait operation specified by the given |waitId|.
+ * @param {MojoWaitId} waitId The waitId returned by asyncWait.
+ */
+function cancelWait(waitId) { [native code] }
diff --git a/mojo/public/js/bindings/tests/validation_test_input_parser.js b/mojo/public/js/bindings/tests/validation_test_input_parser.js
new file mode 100644
index 0000000..98b1c19
--- /dev/null
+++ b/mojo/public/js/bindings/tests/validation_test_input_parser.js
@@ -0,0 +1,299 @@
+// 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.
+
+// Support for parsing binary sequences encoded as readable strings
+// or ".data" files. The input format is described here:
+// mojo/public/cpp/bindings/tests/validation_test_input_parser.h
+
+define([
+ "mojo/public/js/bindings/buffer"
+ ], function(buffer) {
+
+ // Files and Lines represent the raw text from an input string
+ // or ".data" file.
+
+ function InputError(message, line) {
+ this.message = message;
+ this.line = line;
+ }
+
+ InputError.prototype.toString = function() {
+ var s = 'Error: ' + this.message;
+ if (this.line)
+ s += ', at line ' +
+ (this.line.number + 1) + ': "' + this.line.contents + '"';
+ return s;
+ }
+
+ function File(contents) {
+ this.contents = contents;
+ this.index = 0;
+ this.lineNumber = 0;
+ }
+
+ File.prototype.endReached = function() {
+ return this.index >= this.contents.length;
+ }
+
+ File.prototype.nextLine = function() {
+ if (this.endReached())
+ return null;
+ var start = this.index;
+ var end = this.contents.indexOf('\n', start);
+ if (end == -1)
+ end = this.contents.length;
+ this.index = end + 1;
+ return new Line(this.contents.substring(start, end), this.lineNumber++);
+ }
+
+ function Line(contents, number) {
+ var i = contents.indexOf('//');
+ var s = (i == -1) ? contents.trim() : contents.substring(0, i).trim();
+ this.contents = contents;
+ this.items = (s.length > 0) ? s.split(/\s+/) : [];
+ this.index = 0;
+ this.number = number;
+ }
+
+ Line.prototype.endReached = function() {
+ return this.index >= this.items.length;
+ }
+
+ var ITEM_TYPE_SIZES = {
+ u1: 1, u2: 2, u4: 4, u8: 8, s1: 1, s2: 2, s4: 4, s8: 8, b: 1, f: 4, d: 8,
+ dist4: 4, dist8: 8, anchr: 0, handles: 0
+ };
+
+ function isValidItemType(type) {
+ return ITEM_TYPE_SIZES[type] !== undefined;
+ }
+
+ Line.prototype.nextItem = function() {
+ if (this.endReached())
+ return null;
+
+ var itemString = this.items[this.index++];
+ var type = 'u1';
+ var value = itemString;
+
+ if (itemString.charAt(0) == '[') {
+ var i = itemString.indexOf(']');
+ if (i != -1 && i + 1 < itemString.length) {
+ type = itemString.substring(1, i);
+ value = itemString.substring(i + 1);
+ } else {
+ throw new InputError('invalid item', this);
+ }
+ }
+ if (!isValidItemType(type))
+ throw new InputError('invalid item type', this);
+
+ return new Item(this, type, value);
+ }
+
+ // The text for each whitespace delimited binary data "item" is represented
+ // by an Item.
+
+ function Item(line, type, value) {
+ this.line = line;
+ this.type = type;
+ this.value = value;
+ this.size = ITEM_TYPE_SIZES[type];
+ }
+
+ Item.prototype.isFloat = function() {
+ return this.type == 'f' || this.type == 'd';
+ }
+
+ Item.prototype.isInteger = function() {
+ return ['u1', 'u2', 'u4', 'u8',
+ 's1', 's2', 's4', 's8'].indexOf(this.type) != -1;
+ }
+
+ Item.prototype.isNumber = function() {
+ return this.isFloat() || this.isInteger();
+ }
+
+ Item.prototype.isByte = function() {
+ return this.type == 'b';
+ }
+
+ Item.prototype.isDistance = function() {
+ return this.type == 'dist4' || this.type == 'dist8';
+ }
+
+ Item.prototype.isAnchor = function() {
+ return this.type == 'anchr';
+ }
+
+ Item.prototype.isHandles = function() {
+ return this.type == 'handles';
+ }
+
+ // A TestMessage represents the complete binary message loaded from an input
+ // string or ".data" file. The parseTestMessage() function below constructs
+ // a TestMessage from a File.
+
+ function TestMessage(byteLength) {
+ this.index = 0;
+ this.buffer = new buffer.Buffer(byteLength);
+ this.distances = {};
+ this.handleCount = 0;
+ }
+
+ function checkItemNumberValue(item, n, min, max) {
+ if (n < min || n > max)
+ throw new InputError('invalid item value', item.line);
+ }
+
+ TestMessage.prototype.addNumber = function(item) {
+ var n = item.isInteger() ? parseInt(item.value) : parseFloat(item.value);
+ if (Number.isNaN(n))
+ throw new InputError("can't parse item value", item.line);
+
+ switch(item.type) {
+ case 'u1':
+ checkItemNumberValue(item, n, 0, 0xFF);
+ this.buffer.setUint8(this.index, n);
+ break;
+ case 'u2':
+ checkItemNumberValue(item, n, 0, 0xFFFF);
+ this.buffer.setUint16(this.index, n);
+ break;
+ case 'u4':
+ checkItemNumberValue(item, n, 0, 0xFFFFFFFF);
+ this.buffer.setUint32(this.index, n);
+ break;
+ case 'u8':
+ checkItemNumberValue(item, n, 0, Number.MAX_SAFE_INTEGER);
+ this.buffer.setUint64(this.index, n);
+ break;
+ case 's1':
+ checkItemNumberValue(item, n, -128, 127);
+ this.buffer.setInt8(this.index, n);
+ break;
+ case 's2':
+ checkItemNumberValue(item, n, -32768, 32767);
+ this.buffer.setInt16(this.index, n);
+ break;
+ case 's4':
+ checkItemNumberValue(item, n, -2147483648, 2147483647);
+ this.buffer.setInt32(this.index, n);
+ break;
+ case 's8':
+ checkItemNumberValue(item, n,
+ Number.MIN_SAFE_INTEGER,
+ Number.MAX_SAFE_INTEGER);
+ this.buffer.setInt64(this.index, n);
+ break;
+ case 'f':
+ this.buffer.setFloat32(this.index, n);
+ break;
+ case 'd':
+ this.buffer.setFloat64(this.index, n);
+ break;
+
+ default:
+ throw new InputError('unrecognized item type', item.line);
+ }
+ }
+
+ TestMessage.prototype.addByte = function(item) {
+ if (!/^[01]{8}$/.test(item.value))
+ throw new InputError('invalid byte item value', item.line);
+ function b(i) {
+ return (item.value.charAt(7 - i) == '1') ? 1 << i : 0;
+ }
+ var n = b(0) | b(1) | b(2) | b(3) | b(4) | b(5) | b(6) | b(7);
+ this.buffer.setUint8(this.index, n);
+ }
+
+ TestMessage.prototype.addDistance = function(item) {
+ if (this.distances[item.value])
+ throw new InputError('duplicate distance item', item.line);
+ this.distances[item.value] = {index: this.index, item: item};
+ }
+
+ TestMessage.prototype.addAnchor = function(item) {
+ var dist = this.distances[item.value];
+ if (!dist)
+ throw new InputError('unmatched anchor item', item.line);
+ delete this.distances[item.value];
+
+ var n = this.index - dist.index;
+ // TODO(hansmuller): validate n
+
+ if (dist.item.type == 'dist4')
+ this.buffer.setUint32(dist.index, n);
+ else if (dist.item.type == 'dist8')
+ this.buffer.setUint64(dist.index, n);
+ else
+ throw new InputError('unrecognzed distance item type', dist.item.line);
+ }
+
+ TestMessage.prototype.addHandles = function(item) {
+ this.handleCount = parseInt(item.value);
+ if (Number.isNaN(this.handleCount))
+ throw new InputError("can't parse handleCount", item.line);
+ }
+
+ TestMessage.prototype.addItem = function(item) {
+ if (item.isNumber())
+ this.addNumber(item);
+ else if (item.isByte())
+ this.addByte(item);
+ else if (item.isDistance())
+ this.addDistance(item);
+ else if (item.isAnchor())
+ this.addAnchor(item);
+ else if (item.isHandles())
+ this.addHandles(item);
+ else
+ throw new InputError('unrecognized item type', item.line);
+
+ this.index += item.size;
+ }
+
+ TestMessage.prototype.unanchoredDistances = function() {
+ var names = null;
+ for (var name in this.distances) {
+ if (this.distances.hasOwnProperty(name))
+ names = (names === null) ? name : names + ' ' + name;
+ }
+ return names;
+ }
+
+ function parseTestMessage(text) {
+ var file = new File(text);
+ var items = [];
+ var messageLength = 0;
+ while(!file.endReached()) {
+ var line = file.nextLine();
+ while (!line.endReached()) {
+ var item = line.nextItem();
+ if (item.isHandles() && items.length > 0)
+ throw new InputError('handles item is not first');
+ messageLength += item.size;
+ items.push(item);
+ }
+ }
+
+ var msg = new TestMessage(messageLength);
+ for (var i = 0; i < items.length; i++)
+ msg.addItem(items[i]);
+
+ if (messageLength != msg.index)
+ throw new InputError('failed to compute message length');
+ var names = msg.unanchoredDistances();
+ if (names)
+ throw new InputError('no anchors for ' + names, 0);
+
+ return msg;
+ }
+
+ var exports = {};
+ exports.parseTestMessage = parseTestMessage;
+ exports.InputError = InputError;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/unicode.js b/mojo/public/js/bindings/unicode.js
new file mode 100644
index 0000000..ba0f00f
--- /dev/null
+++ b/mojo/public/js/bindings/unicode.js
@@ -0,0 +1,51 @@
+// 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.
+
+/**
+ * Defines functions for translating between JavaScript strings and UTF8 strings
+ * stored in ArrayBuffers. There is much room for optimization in this code if
+ * it proves necessary.
+ */
+define("mojo/public/js/bindings/unicode", function() {
+ /**
+ * Decodes the UTF8 string from the given buffer.
+ * @param {ArrayBufferView} buffer The buffer containing UTF8 string data.
+ * @return {string} The corresponding JavaScript string.
+ */
+ function decodeUtf8String(buffer) {
+ return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer)));
+ }
+
+ /**
+ * Encodes the given JavaScript string into UTF8.
+ * @param {string} str The string to encode.
+ * @param {ArrayBufferView} outputBuffer The buffer to contain the result.
+ * Should be pre-allocated to hold enough space. Use |utf8Length| to determine
+ * how much space is required.
+ * @return {number} The number of bytes written to |outputBuffer|.
+ */
+ function encodeUtf8String(str, outputBuffer) {
+ var utf8String = unescape(encodeURIComponent(str));
+ if (outputBuffer.length < utf8String.length)
+ throw new Error("Buffer too small for encodeUtf8String");
+ for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++)
+ outputBuffer[i] = utf8String.charCodeAt(i);
+ return i;
+ }
+
+ /**
+ * Returns the number of bytes that a UTF8 encoding of the JavaScript string
+ * |str| would occupy.
+ */
+ function utf8Length(str) {
+ var utf8String = unescape(encodeURIComponent(str));
+ return utf8String.length;
+ }
+
+ var exports = {};
+ exports.decodeUtf8String = decodeUtf8String;
+ exports.encodeUtf8String = encodeUtf8String;
+ exports.utf8Length = utf8Length;
+ return exports;
+});
diff --git a/mojo/public/js/bindings/validation_unittests.js b/mojo/public/js/bindings/validation_unittests.js
new file mode 100644
index 0000000..73fd9c7
--- /dev/null
+++ b/mojo/public/js/bindings/validation_unittests.js
@@ -0,0 +1,302 @@
+// 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([
+ "console",
+ "file",
+ "gin/test/expect",
+ "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
+ "mojo/public/js/bindings/buffer",
+ "mojo/public/js/bindings/codec",
+ "mojo/public/js/bindings/connection",
+ "mojo/public/js/bindings/connector",
+ "mojo/public/js/bindings/core",
+ "mojo/public/js/bindings/tests/validation_test_input_parser",
+ "mojo/public/js/bindings/router",
+ "mojo/public/js/bindings/validator",
+], function(console,
+ file,
+ expect,
+ testInterface,
+ buffer,
+ codec,
+ connection,
+ connector,
+ core,
+ parser,
+ router,
+ validator) {
+
+ var noError = validator.validationError.NONE;
+
+ function checkTestMessageParser() {
+ function TestMessageParserFailure(message, input) {
+ this.message = message;
+ this.input = input;
+ }
+
+ TestMessageParserFailure.prototype.toString = function() {
+ return 'Error: ' + this.message + ' for "' + this.input + '"';
+ }
+
+ function checkData(data, expectedData, input) {
+ if (data.byteLength != expectedData.byteLength) {
+ var s = "message length (" + data.byteLength + ") doesn't match " +
+ "expected length: " + expectedData.byteLength;
+ throw new TestMessageParserFailure(s, input);
+ }
+
+ for (var i = 0; i < data.byteLength; i++) {
+ if (data.getUint8(i) != expectedData.getUint8(i)) {
+ var s = 'message data mismatch at byte offset ' + i;
+ throw new TestMessageParserFailure(s, input);
+ }
+ }
+ }
+
+ function testFloatItems() {
+ var input = '[f]+.3e9 [d]-10.03';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(12);
+ expectedData.setFloat32(0, +.3e9);
+ expectedData.setFloat64(4, -10.03);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testUnsignedIntegerItems() {
+ var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' +
+ '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(17);
+ expectedData.setUint8(0, 0x10);
+ expectedData.setUint16(1, 65535);
+ expectedData.setUint32(3, 65536);
+ expectedData.setUint64(7, 0xFFFFFFFFFFFFF);
+ expectedData.setUint8(15, 0);
+ expectedData.setUint8(16, 0xff);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testSignedIntegerItems() {
+ var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(15);
+ expectedData.setInt64(0, -0x800);
+ expectedData.setInt8(8, -128);
+ expectedData.setInt16(9, 0);
+ expectedData.setInt32(11, -40);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testByteItems() {
+ var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(3);
+ expectedData.setUint8(0, 11);
+ expectedData.setUint8(1, 128);
+ expectedData.setUint8(2, 0);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testAnchors() {
+ var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(14);
+ expectedData.setUint32(0, 14);
+ expectedData.setUint8(4, 0);
+ expectedData.setUint64(5, 9);
+ expectedData.setUint8(13, 0);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testHandles() {
+ var input = '// This message has handles! \n[handles]50 [u8]2';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(8);
+ expectedData.setUint64(0, 2);
+
+ if (msg.handleCount != 50) {
+ var s = 'wrong handle count (' + msg.handleCount + ')';
+ throw new TestMessageParserFailure(s, input);
+ }
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testEmptyInput() {
+ var msg = parser.parseTestMessage('');
+ if (msg.buffer.byteLength != 0)
+ throw new TestMessageParserFailure('expected empty message', '');
+ }
+
+ function testBlankInput() {
+ var input = ' \t // hello world \n\r \t// the answer is 42 ';
+ var msg = parser.parseTestMessage(input);
+ if (msg.buffer.byteLength != 0)
+ throw new TestMessageParserFailure('expected empty message', input);
+ }
+
+ function testInvalidInput() {
+ function parserShouldFail(input) {
+ try {
+ parser.parseTestMessage(input);
+ } catch (e) {
+ if (e instanceof parser.InputError)
+ return;
+ throw new TestMessageParserFailure(
+ 'unexpected exception ' + e.toString(), input);
+ }
+ throw new TestMessageParserFailure("didn't detect invalid input", file);
+ }
+
+ ['/ 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'
+ ].forEach(parserShouldFail);
+ }
+
+ try {
+ testFloatItems();
+ testUnsignedIntegerItems();
+ testSignedIntegerItems();
+ testByteItems();
+ testInvalidInput();
+ testEmptyInput();
+ testBlankInput();
+ testHandles();
+ testAnchors();
+ } catch (e) {
+ return e.toString();
+ }
+ return null;
+ }
+
+ function getMessageTestFiles(key) {
+ var sourceRoot = file.getSourceRootDirectory();
+ expect(sourceRoot).not.toBeNull();
+
+ var testDir = sourceRoot +
+ "/mojo/public/interfaces/bindings/tests/data/validation/";
+ var testFiles = file.getFilesInDirectory(testDir);
+ expect(testFiles).not.toBeNull();
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ // The matching ".data" pathnames with the extension removed.
+ return testFiles.filter(function(s) {
+ return s.substr(-5) == ".data";
+ }).map(function(s) {
+ return testDir + s.slice(0, -5);
+ }).filter(function(s) {
+ return s.indexOf(key) != -1;
+ });
+ }
+
+ function readTestMessage(filename) {
+ var contents = file.readFileToString(filename + ".data");
+ expect(contents).not.toBeNull();
+ return parser.parseTestMessage(contents);
+ }
+
+ function readTestExpected(filename) {
+ var contents = file.readFileToString(filename + ".expected");
+ expect(contents).not.toBeNull();
+ return contents.trim();
+ }
+
+ function checkValidationResult(testFile, err) {
+ var actualResult = (err === noError) ? "PASS" : err;
+ var expectedResult = readTestExpected(testFile);
+ if (actualResult != expectedResult)
+ console.log("[Test message validation failed: " + testFile + " ]");
+ expect(actualResult).toEqual(expectedResult);
+ }
+
+ function testMessageValidation(key, filters) {
+ var testFiles = getMessageTestFiles(key);
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ for (var i = 0; i < testFiles.length; i++) {
+ // TODO(hansmuller): Temporarily skipping array pointer overflow tests.
+ if (testFiles[i].indexOf("overflow") != -1) {
+ console.log("[Skipping " + testFiles[i] + "]");
+ continue;
+ }
+
+ var testMessage = readTestMessage(testFiles[i]);
+ var handles = new Array(testMessage.handleCount);
+ var message = new codec.Message(testMessage.buffer, handles);
+ var messageValidator = new validator.Validator(message);
+
+ var err = messageValidator.validateMessageHeader();
+ for (var j = 0; err === noError && j < filters.length; ++j)
+ err = filters[j](messageValidator);
+
+ checkValidationResult(testFiles[i], err);
+ }
+ }
+
+ function testConformanceMessageValidation() {
+ testMessageValidation("conformance_", [
+ testInterface.ConformanceTestInterfaceStub.prototype.validator]);
+ }
+
+ function testNotImplementedMessageValidation() {
+ testMessageValidation("not_implemented_", [
+ testInterface.ConformanceTestInterfaceStub.prototype.validator]);
+ }
+
+ function testIntegratedMessageValidation() {
+ var testFiles = getMessageTestFiles("integration_");
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ for (var i = 0; i < testFiles.length; i++) {
+ // TODO(hansmuller): Temporarily skipping array pointer overflow tests.
+ if (testFiles[i].indexOf("overflow") != -1) {
+ console.log("[Skipping " + testFiles[i] + "]");
+ continue;
+ }
+
+ var testMessage = readTestMessage(testFiles[i]);
+ var handles = new Array(testMessage.handleCount);
+ var testMessagePipe = new core.createMessagePipe();
+ expect(testMessagePipe.result).toBe(core.RESULT_OK);
+
+ var writeMessageValue = core.writeMessage(
+ testMessagePipe.handle0,
+ new Uint8Array(testMessage.buffer.arrayBuffer),
+ new Array(testMessage.handleCount),
+ core.WRITE_MESSAGE_FLAG_NONE);
+ expect(writeMessageValue).toBe(core.RESULT_OK);
+
+ var testConnection = new connection.TestConnection(
+ testMessagePipe.handle1,
+ testInterface.IntegrationTestInterface1Stub,
+ testInterface.IntegrationTestInterface2Proxy);
+
+ var validationError = noError;
+ testConnection.router_.validationErrorHandler = function(err) {
+ validationError = err;
+ }
+
+ testConnection.router_.connector_.deliverMessage();
+ checkValidationResult(testFiles[i], validationError);
+
+ testConnection.close();
+ expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK);
+ }
+ }
+
+ expect(checkTestMessageParser()).toBeNull();
+ testConformanceMessageValidation();
+ testIntegratedMessageValidation();
+ this.result = "PASS";
+});
diff --git a/mojo/public/js/bindings/validator.js b/mojo/public/js/bindings/validator.js
new file mode 100644
index 0000000..de75656
--- /dev/null
+++ b/mojo/public/js/bindings/validator.js
@@ -0,0 +1,291 @@
+// 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("mojo/public/js/bindings/validator", [
+ "mojo/public/js/bindings/codec",
+], function(codec) {
+
+ var validationError = {
+ NONE: 'VALIDATION_ERROR_NONE',
+ MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
+ ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
+ UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
+ UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
+ ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
+ UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
+ ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
+ UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
+ MESSAGE_HEADER_INVALID_FLAG_COMBINATION:
+ 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION',
+ MESSAGE_HEADER_MISSING_REQUEST_ID:
+ 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID'
+ };
+
+ var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
+
+ function isStringClass(cls) {
+ return cls === codec.String || cls === codec.NullableString;
+ }
+
+ function isHandleClass(cls) {
+ return cls === codec.Handle || cls === codec.NullableHandle;
+ }
+
+ function isNullable(type) {
+ return type === codec.NullableString || type === codec.NullableHandle ||
+ type instanceof codec.NullableArrayOf ||
+ type instanceof codec.NullablePointerTo;
+ }
+
+ function Validator(message) {
+ this.message = message;
+ this.offset = 0;
+ this.handleIndex = 0;
+ }
+
+ Object.defineProperty(Validator.prototype, "offsetLimit", {
+ get: function() { return this.message.buffer.byteLength; }
+ });
+
+ Object.defineProperty(Validator.prototype, "handleIndexLimit", {
+ get: function() { return this.message.handles.length; }
+ });
+
+ // True if we can safely allocate a block of bytes from start to
+ // to start + numBytes.
+ Validator.prototype.isValidRange = function(start, numBytes) {
+ // Only positive JavaScript integers that are less than 2^53
+ // (Number.MAX_SAFE_INTEGER) can be represented exactly.
+ if (start < this.offset || numBytes <= 0 ||
+ !Number.isSafeInteger(start) ||
+ !Number.isSafeInteger(numBytes))
+ return false;
+
+ var newOffset = start + numBytes;
+ if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
+ return false;
+
+ return true;
+ }
+
+ Validator.prototype.claimRange = function(start, numBytes) {
+ if (this.isValidRange(start, numBytes)) {
+ this.offset = start + numBytes;
+ return true;
+ }
+ return false;
+ }
+
+ Validator.prototype.claimHandle = function(index) {
+ if (index === codec.kEncodedInvalidHandleValue)
+ return true;
+
+ if (index < this.handleIndex || index >= this.handleIndexLimit)
+ return false;
+
+ // This is safe because handle indices are uint32.
+ this.handleIndex = index + 1;
+ return true;
+ }
+
+ Validator.prototype.validateHandle = function(offset, nullable) {
+ var index = this.message.buffer.getUint32(offset);
+
+ if (index === codec.kEncodedInvalidHandleValue)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
+
+ if (!this.claimHandle(index))
+ return validationError.ILLEGAL_HANDLE;
+ return validationError.NONE;
+ }
+
+ Validator.prototype.validateStructHeader =
+ function(offset, minNumBytes, minNumFields) {
+ if (!codec.isAligned(offset))
+ return validationError.MISALIGNED_OBJECT;
+
+ if (!this.isValidRange(offset, codec.kStructHeaderSize))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ var numBytes = this.message.buffer.getUint32(offset);
+ var numFields = this.message.buffer.getUint32(offset + 4);
+
+ if (numBytes < minNumBytes || numFields < minNumFields)
+ return validationError.UNEXPECTED_STRUCT_HEADER;
+
+ if (!this.claimRange(offset, numBytes))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ return validationError.NONE;
+ }
+
+ Validator.prototype.validateMessageHeader = function() {
+ var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 2);
+ if (err != validationError.NONE)
+ return err;
+
+ var numBytes = this.message.getHeaderNumBytes();
+ var numFields = this.message.getHeaderNumFields();
+
+ var validNumFieldsAndNumBytes =
+ (numFields == 2 && numBytes == codec.kMessageHeaderSize) ||
+ (numFields == 3 &&
+ numBytes == codec.kMessageWithRequestIDHeaderSize) ||
+ (numFields > 3 &&
+ numBytes >= codec.kMessageWithRequestIDHeaderSize);
+ if (!validNumFieldsAndNumBytes)
+ return validationError.UNEXPECTED_STRUCT_HEADER;
+
+ var expectsResponse = this.message.expectsResponse();
+ var isResponse = this.message.isResponse();
+
+ if (numFields == 2 && (expectsResponse || isResponse))
+ return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
+
+ if (isResponse && expectsResponse)
+ return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION;
+
+ return validationError.NONE;
+ }
+
+ // Returns the message.buffer relative offset this pointer "points to",
+ // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
+ // pointer's value is not valid.
+ Validator.prototype.decodePointer = function(offset) {
+ var pointerValue = this.message.buffer.getUint64(offset);
+ if (pointerValue === 0)
+ return NULL_MOJO_POINTER;
+ var bufferOffset = offset + pointerValue;
+ return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
+ }
+
+ Validator.prototype.validateArrayPointer = function(
+ offset, elementSize, expectedElementCount, elementType, nullable) {
+ var arrayOffset = this.decodePointer(offset);
+ if (arrayOffset === null)
+ return validationError.ILLEGAL_POINTER;
+
+ if (arrayOffset === NULL_MOJO_POINTER)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+ return this.validateArray(
+ arrayOffset, elementSize, expectedElementCount, elementType);
+ }
+
+ Validator.prototype.validateStructPointer = function(
+ offset, structClass, nullable) {
+ var structOffset = this.decodePointer(offset);
+ if (structOffset === null)
+ return validationError.ILLEGAL_POINTER;
+
+ if (structOffset === NULL_MOJO_POINTER)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+ return structClass.validate(this, structOffset);
+ }
+
+ Validator.prototype.validateStringPointer = function(offset, nullable) {
+ return this.validateArrayPointer(
+ offset, codec.Uint8.encodedSize, 0, codec.Uint8, nullable);
+ }
+
+ // Similar to Array_Data<T>::Validate()
+ // mojo/public/cpp/bindings/lib/array_internal.h
+
+ Validator.prototype.validateArray =
+ function (offset, elementSize, expectedElementCount, elementType) {
+ if (!codec.isAligned(offset))
+ return validationError.MISALIGNED_OBJECT;
+
+ if (!this.isValidRange(offset, codec.kArrayHeaderSize))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ var numBytes = this.message.buffer.getUint32(offset);
+ var numElements = this.message.buffer.getUint32(offset + 4);
+
+ // Note: this computation is "safe" because elementSize <= 8 and
+ // numElements is a uint32.
+ var elementsTotalSize = (elementType === codec.PackedBool) ?
+ Math.ceil(numElements / 8) : (elementSize * numElements);
+
+ if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
+ return validationError.UNEXPECTED_ARRAY_HEADER;
+
+ if (expectedElementCount != 0 && numElements != expectedElementCount)
+ return validationError.UNEXPECTED_ARRAY_HEADER;
+
+ if (!this.claimRange(offset, numBytes))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ // Validate the array's elements if they are pointers or handles.
+
+ var elementsOffset = offset + codec.kArrayHeaderSize;
+ var nullable = isNullable(elementType);
+
+ if (isHandleClass(elementType))
+ return this.validateHandleElements(elementsOffset, numElements, nullable);
+ if (isStringClass(elementType))
+ return this.validateArrayElements(
+ elementsOffset, numElements, codec.Uint8, nullable)
+ if (elementType instanceof codec.PointerTo)
+ return this.validateStructElements(
+ elementsOffset, numElements, elementType.cls, nullable);
+ if (elementType instanceof codec.ArrayOf)
+ return this.validateArrayElements(
+ elementsOffset, numElements, elementType.cls, nullable);
+
+ return validationError.NONE;
+ }
+
+ // Note: the |offset + i * elementSize| computation in the validateFooElements
+ // methods below is "safe" because elementSize <= 8, offset and
+ // numElements are uint32, and 0 <= i < numElements.
+
+ Validator.prototype.validateHandleElements =
+ function(offset, numElements, nullable) {
+ var elementSize = codec.Handle.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateHandle(elementOffset, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ }
+
+ // The elementClass parameter is the element type of the element arrays.
+ Validator.prototype.validateArrayElements =
+ function(offset, numElements, elementClass, nullable) {
+ var elementSize = codec.PointerTo.prototype.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateArrayPointer(
+ elementOffset, elementClass.encodedSize, 0, elementClass, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ }
+
+ Validator.prototype.validateStructElements =
+ function(offset, numElements, structClass, nullable) {
+ var elementSize = codec.PointerTo.prototype.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err =
+ this.validateStructPointer(elementOffset, structClass, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ }
+
+ var exports = {};
+ exports.validationError = validationError;
+ exports.Validator = Validator;
+ return exports;
+});
diff --git a/mojo/public/platform/native/BUILD.gn b/mojo/public/platform/native/BUILD.gn
new file mode 100644
index 0000000..a3e720b
--- /dev/null
+++ b/mojo/public/platform/native/BUILD.gn
@@ -0,0 +1,59 @@
+# 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.
+
+source_set("system_thunks") {
+ visibility = [
+ "//mojo/public/c/system:for_component",
+ "//mojo/public/c/system:for_shared_library",
+ ]
+
+ sources = [
+ "system_thunks.h",
+ "system_thunks.cc",
+ ]
+ defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
+ deps = [ "//mojo/public/c/system" ]
+
+ # The GYP target analogous to this one builds this code into a
+ # static library. When building for Android, both the GYP and GN
+ # builds add --exclude-libs=ALL globally, which means that all
+ # symbols in static libraries are excluded from export. That's a
+ # problem, as code outside this target needs to be able to call
+ # MojoSetSystemThunks(). Therefore, the GYP target needs to specifiy
+ # that all dependent targets remove that link flag. Since GN uses a
+ # source_set here, this flag change is not needed.
+}
+
+# GYP version: mojo/mojo_public.gypi:mojo_gles2
+source_set("gles2_thunks") {
+ visibility = [ "//mojo/public/gles2:for_shared_library" ]
+
+ sources = [
+ "gles2_thunks.cc",
+ "gles2_thunks.h",
+ "gles2_impl_thunks.cc",
+ "gles2_impl_thunks.h",
+ "gles2_impl_chromium_texture_mailbox_thunks.cc",
+ "gles2_impl_chromium_texture_mailbox_thunks.h",
+ "gles2_impl_chromium_sync_point_thunks.cc",
+ "gles2_impl_chromium_sync_point_thunks.h",
+ ]
+
+ defines = [
+ "MOJO_GLES2_IMPLEMENTATION",
+ ]
+
+ configs += [ "//third_party/khronos:khronos_headers" ]
+
+ deps = [
+ "//mojo/public/c/gles2",
+ "//mojo/public/c/environment",
+ "//mojo/public/c/system",
+ ]
+
+ if (is_mac) {
+ # TODO(GYP): Make it a run-path dependent library.
+ # 'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+ }
+}
diff --git a/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc
new file mode 100644
index 0000000..0a47f33
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc
@@ -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.
+
+#include "mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplChromiumSyncPointThunks g_impl_chromium_sync_point_thunks =
+ {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType gl##Function PARAMETERS { \
+ assert(g_impl_chromium_sync_point_thunks.Function); \
+ return g_impl_chromium_sync_point_thunks.Function ARGUMENTS; \
+ }
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumSyncPointThunks(
+ const MojoGLES2ImplChromiumSyncPointThunks*
+ gles2_impl_chromium_sync_point_thunks) {
+ if (gles2_impl_chromium_sync_point_thunks->size >=
+ sizeof(g_impl_chromium_sync_point_thunks))
+ g_impl_chromium_sync_point_thunks = *gles2_impl_chromium_sync_point_thunks;
+ return sizeof(g_impl_chromium_sync_point_thunks);
+}
+
+} // extern "C"
diff --git a/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h
new file mode 100644
index 0000000..af6817e
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h
@@ -0,0 +1,44 @@
+// 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_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/chromium_sync_point.h"
+
+// Specifies the frozen API for the GLES2 CHROMIUM_sync_point extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplChromiumSyncPointThunks {
+ size_t size; // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplChromiumSyncPointThunks
+MojoMakeGLES2ImplChromiumSyncPointThunks() {
+ MojoGLES2ImplChromiumSyncPointThunks gles2_impl_chromium_sync_point_thunks = {
+ sizeof(MojoGLES2ImplChromiumSyncPointThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h"
+#undef VISIT_GL_CALL
+ };
+
+ return gles2_impl_chromium_sync_point_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_chromium_sync_point_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplChromiumSyncPointThunksFn)(
+ const MojoGLES2ImplChromiumSyncPointThunks* thunks);
+
+#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_
diff --git a/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc
new file mode 100644
index 0000000..db2bc81
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc
@@ -0,0 +1,33 @@
+// 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/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplChromiumTextureMailboxThunks
+ g_impl_chromium_texture_mailbox_thunks = {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType gl##Function PARAMETERS { \
+ assert(g_impl_chromium_texture_mailbox_thunks.Function); \
+ return g_impl_chromium_texture_mailbox_thunks.Function ARGUMENTS; \
+ }
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumTextureMailboxThunks(
+ const MojoGLES2ImplChromiumTextureMailboxThunks*
+ gles2_impl_chromium_texture_mailbox_thunks) {
+ if (gles2_impl_chromium_texture_mailbox_thunks->size >=
+ sizeof(g_impl_chromium_texture_mailbox_thunks))
+ g_impl_chromium_texture_mailbox_thunks =
+ *gles2_impl_chromium_texture_mailbox_thunks;
+ return sizeof(g_impl_chromium_texture_mailbox_thunks);
+}
+
+} // extern "C"
diff --git a/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h
new file mode 100644
index 0000000..839e441
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/chromium_texture_mailbox.h"
+
+// Specifies the frozen API for the GLES2 CHROMIUM_texture_mailbox extension.
+#pragma pack(push, 8)
+struct MojoGLES2ImplChromiumTextureMailboxThunks {
+ size_t size; // Should be set to sizeof(*this).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplChromiumTextureMailboxThunks
+MojoMakeGLES2ImplChromiumTextureMailboxThunks() {
+ MojoGLES2ImplChromiumTextureMailboxThunks
+ gles2_impl_chromium_texture_mailbox_thunks = {
+ sizeof(MojoGLES2ImplChromiumTextureMailboxThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h"
+#undef VISIT_GL_CALL
+ };
+
+ return gles2_impl_chromium_texture_mailbox_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system.
+// The contents of |gles2_impl_chromium_texture_mailbox_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplChromiumTextureMailboxThunksFn)(
+ const MojoGLES2ImplChromiumTextureMailboxThunks* thunks);
+
+#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_
diff --git a/mojo/public/platform/native/gles2_impl_thunks.cc b/mojo/public/platform/native/gles2_impl_thunks.cc
new file mode 100644
index 0000000..66dbc39
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_thunks.cc
@@ -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.
+
+#include "mojo/public/platform/native/gles2_impl_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+static MojoGLES2ImplThunks g_impl_thunks = {0};
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType gl##Function PARAMETERS { \
+ assert(g_impl_thunks.Function); \
+ return g_impl_thunks.Function ARGUMENTS; \
+ }
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+extern "C" THUNK_EXPORT size_t
+MojoSetGLES2ImplThunks(const MojoGLES2ImplThunks* gles2_impl_thunks) {
+ if (gles2_impl_thunks->size >= sizeof(g_impl_thunks))
+ g_impl_thunks = *gles2_impl_thunks;
+ return sizeof(g_impl_thunks);
+}
+
+} // extern "C"
diff --git a/mojo/public/platform/native/gles2_impl_thunks.h b/mojo/public/platform/native/gles2_impl_thunks.h
new file mode 100644
index 0000000..04174fa
--- /dev/null
+++ b/mojo/public/platform/native/gles2_impl_thunks.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_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/gles2.h"
+
+// Like MojoGLES2ControlThunks, but specifies the frozen GLES2 API. Separated
+// out as MojoGLES2ControlThunks may be modified and added to, but this
+// interface is frozen.
+#pragma pack(push, 8)
+struct MojoGLES2ImplThunks {
+ size_t size; // Should be set to sizeof(MojoGLES2ImplThunks).
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType(*Function) PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder to get the embedder's implementation
+// of GLES2.
+inline MojoGLES2ImplThunks MojoMakeGLES2ImplThunks() {
+ MojoGLES2ImplThunks gles2_impl_thunks = {
+ sizeof(MojoGLES2ImplThunks),
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function,
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+ };
+
+ return gles2_impl_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetGLES2ImplThunksFn mojo_set_gles2_impl_thunks_fn =
+// reinterpret_cast<MojoSetGLES2ImplThunksFn>(
+// app_library.GetFunctionPointer("MojoSetGLES2ImplThunks"));
+// The expected size of |gles2_impl_thunks| is returned.
+// The contents of |gles2_impl_thunks| are copied.
+typedef size_t (*MojoSetGLES2ImplThunksFn)(
+ const MojoGLES2ImplThunks* gles2_impl_thunks);
+
+#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_
diff --git a/mojo/public/platform/native/gles2_thunks.cc b/mojo/public/platform/native/gles2_thunks.cc
new file mode 100644
index 0000000..365fac9
--- /dev/null
+++ b/mojo/public/platform/native/gles2_thunks.cc
@@ -0,0 +1,56 @@
+// 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/platform/native/gles2_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+
+static MojoGLES2ControlThunks g_control_thunks = {0};
+
+MojoGLES2Context MojoGLES2CreateContext(MojoHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ void* closure,
+ const MojoAsyncWaiter* async_waiter) {
+ assert(g_control_thunks.GLES2CreateContext);
+ return g_control_thunks.GLES2CreateContext(
+ handle, lost_callback, closure, async_waiter);
+}
+
+void MojoGLES2DestroyContext(MojoGLES2Context context) {
+ assert(g_control_thunks.GLES2DestroyContext);
+ g_control_thunks.GLES2DestroyContext(context);
+}
+
+void MojoGLES2MakeCurrent(MojoGLES2Context context) {
+ assert(g_control_thunks.GLES2MakeCurrent);
+ g_control_thunks.GLES2MakeCurrent(context);
+}
+
+void MojoGLES2SwapBuffers() {
+ assert(g_control_thunks.GLES2SwapBuffers);
+ g_control_thunks.GLES2SwapBuffers();
+}
+
+void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) {
+ assert(g_control_thunks.GLES2GetGLES2Interface);
+ return g_control_thunks.GLES2GetGLES2Interface(context);
+}
+
+void* MojoGLES2GetContextSupport(MojoGLES2Context context) {
+ assert(g_control_thunks.GLES2GetContextSupport);
+ return g_control_thunks.GLES2GetContextSupport(context);
+}
+
+extern "C" THUNK_EXPORT size_t MojoSetGLES2ControlThunks(
+ const MojoGLES2ControlThunks* gles2_control_thunks) {
+ if (gles2_control_thunks->size >= sizeof(g_control_thunks))
+ g_control_thunks = *gles2_control_thunks;
+ return sizeof(g_control_thunks);
+}
+
+} // extern "C"
diff --git a/mojo/public/platform/native/gles2_thunks.h b/mojo/public/platform/native/gles2_thunks.h
new file mode 100644
index 0000000..4718ab3
--- /dev/null
+++ b/mojo/public/platform/native/gles2_thunks.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_PLATFORM_NATIVE_GLES2_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/gles2.h"
+
+// Structure used to bind the interface which manipulates GLES2 surfaces to a
+// DSO to theose of the embedder.
+//
+// This is the ABI between the embedder and the DSO. It can only have new
+// functions added to the end. No other changes are supported.
+#pragma pack(push, 8)
+struct MojoGLES2ControlThunks {
+ size_t size; // Should be set to sizeof(MojoGLES2ControlThunks).
+
+ MojoGLES2Context (*GLES2CreateContext)(MojoHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ void* closure,
+ const MojoAsyncWaiter* async_waiter);
+ void (*GLES2DestroyContext)(MojoGLES2Context context);
+ void (*GLES2MakeCurrent)(MojoGLES2Context context);
+ void (*GLES2SwapBuffers)();
+
+ // TODO(piman): We shouldn't have to leak these 2 interfaces, especially in a
+ // type-unsafe way.
+ void* (*GLES2GetGLES2Interface)(MojoGLES2Context context);
+ void* (*GLES2GetContextSupport)(MojoGLES2Context context);
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder. Returns an object initialized to
+// contain pointers to each of the embedder's MojoGLES2ControlThunks functions.
+inline MojoGLES2ControlThunks MojoMakeGLES2ControlThunks() {
+ MojoGLES2ControlThunks gles2_control_thunks = {
+ sizeof(MojoGLES2ControlThunks),
+ MojoGLES2CreateContext,
+ MojoGLES2DestroyContext,
+ MojoGLES2MakeCurrent,
+ MojoGLES2SwapBuffers,
+ MojoGLES2GetGLES2Interface,
+ MojoGLES2GetContextSupport
+ };
+
+ return gles2_control_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetGLES2ControlThunksFn mojo_set_gles2_control_thunks_fn =
+// reinterpret_cast<MojoSetGLES2ControlThunksFn>(
+// app_library.GetFunctionPointer("MojoSetGLES2ControlThunks"));
+// The expected size of |gles2_control_thunks| is returned.
+// The contents of |gles2_control_thunks| are copied.
+typedef size_t (*MojoSetGLES2ControlThunksFn)(
+ const MojoGLES2ControlThunks* gles2_control_thunks);
+
+#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_
diff --git a/mojo/public/platform/native/system_thunks.cc b/mojo/public/platform/native/system_thunks.cc
new file mode 100644
index 0000000..fed6db9
--- /dev/null
+++ b/mojo/public/platform/native/system_thunks.cc
@@ -0,0 +1,164 @@
+// 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/platform/native/system_thunks.h"
+
+#include <assert.h>
+
+#include "mojo/public/platform/native/thunk_export.h"
+
+extern "C" {
+
+static MojoSystemThunks g_thunks = {0};
+
+MojoTimeTicks MojoGetTimeTicksNow() {
+ assert(g_thunks.GetTimeTicksNow);
+ return g_thunks.GetTimeTicksNow();
+}
+
+MojoResult MojoClose(MojoHandle handle) {
+ assert(g_thunks.Close);
+ return g_thunks.Close(handle);
+}
+
+MojoResult MojoWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ assert(g_thunks.Wait);
+ return g_thunks.Wait(handle, signals, deadline);
+}
+
+MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline) {
+ assert(g_thunks.WaitMany);
+ return g_thunks.WaitMany(handles, signals, num_handles, deadline);
+}
+
+MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1) {
+ assert(g_thunks.CreateMessagePipe);
+ return g_thunks.CreateMessagePipe(options, message_pipe_handle0,
+ message_pipe_handle1);
+}
+
+MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ assert(g_thunks.WriteMessage);
+ return g_thunks.WriteMessage(message_pipe_handle, bytes, num_bytes, handles,
+ num_handles, flags);
+}
+
+MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ assert(g_thunks.ReadMessage);
+ return g_thunks.ReadMessage(message_pipe_handle, bytes, num_bytes, handles,
+ num_handles, flags);
+}
+
+MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle) {
+ assert(g_thunks.CreateDataPipe);
+ return g_thunks.CreateDataPipe(options, data_pipe_producer_handle,
+ data_pipe_consumer_handle);
+}
+
+MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_elements,
+ MojoWriteDataFlags flags) {
+ assert(g_thunks.WriteData);
+ return g_thunks.WriteData(data_pipe_producer_handle, elements, num_elements,
+ flags);
+}
+
+MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoWriteDataFlags flags) {
+ assert(g_thunks.BeginWriteData);
+ return g_thunks.BeginWriteData(data_pipe_producer_handle, buffer,
+ buffer_num_elements, flags);
+}
+
+MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+ uint32_t num_elements_written) {
+ assert(g_thunks.EndWriteData);
+ return g_thunks.EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_elements,
+ MojoReadDataFlags flags) {
+ assert(g_thunks.ReadData);
+ return g_thunks.ReadData(data_pipe_consumer_handle, elements, num_elements,
+ flags);
+}
+
+MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoReadDataFlags flags) {
+ assert(g_thunks.BeginReadData);
+ return g_thunks.BeginReadData(data_pipe_consumer_handle, buffer,
+ buffer_num_elements, flags);
+}
+
+MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_elements_read) {
+ assert(g_thunks.EndReadData);
+ return g_thunks.EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoCreateSharedBuffer(
+ const struct MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle) {
+ assert(g_thunks.CreateSharedBuffer);
+ return g_thunks.CreateSharedBuffer(options, num_bytes, shared_buffer_handle);
+}
+
+MojoResult MojoDuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const struct MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle) {
+ assert(g_thunks.DuplicateBufferHandle);
+ return g_thunks.DuplicateBufferHandle(buffer_handle, options,
+ new_buffer_handle);
+}
+
+MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags) {
+ assert(g_thunks.MapBuffer);
+ return g_thunks.MapBuffer(buffer_handle, offset, num_bytes, buffer, flags);
+}
+
+MojoResult MojoUnmapBuffer(void* buffer) {
+ assert(g_thunks.UnmapBuffer);
+ return g_thunks.UnmapBuffer(buffer);
+}
+
+extern "C" THUNK_EXPORT size_t MojoSetSystemThunks(
+ const MojoSystemThunks* system_thunks) {
+ if (system_thunks->size >= sizeof(g_thunks))
+ g_thunks = *system_thunks;
+ return sizeof(g_thunks);
+}
+
+} // extern "C"
diff --git a/mojo/public/platform/native/system_thunks.h b/mojo/public/platform/native/system_thunks.h
new file mode 100644
index 0000000..d53485c
--- /dev/null
+++ b/mojo/public/platform/native/system_thunks.h
@@ -0,0 +1,145 @@
+// 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.
+
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/system/core.h"
+
+// The embedder needs to bind the basic Mojo Core functions of a DSO to those of
+// the embedder when loading a DSO that is dependent on mojo_system.
+// The typical usage would look like:
+// base::ScopedNativeLibrary app_library(
+// base::LoadNativeLibrary(app_path_, &error));
+// typedef MojoResult (*MojoSetSystemThunksFn)(MojoSystemThunks*);
+// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+// reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+// "MojoSetSystemThunks"));
+// MojoSystemThunks system_thunks = MojoMakeSystemThunks();
+// size_t expected_size = mojo_set_system_thunks_fn(&system_thunks);
+// if (expected_size > sizeof(MojoSystemThunks)) {
+// LOG(ERROR)
+// << "Invalid DSO. Expected MojoSystemThunks size: "
+// << expected_size;
+// break;
+// }
+
+// Structure used to bind the basic Mojo Core functions of a DSO to those of
+// the embedder.
+// This is the ABI between the embedder and the DSO. It can only have new
+// functions added to the end. No other changes are supported.
+#pragma pack(push, 8)
+struct MojoSystemThunks {
+ size_t size; // Should be set to sizeof(MojoSystemThunks).
+ MojoTimeTicks (*GetTimeTicksNow)();
+ MojoResult (*Close)(MojoHandle handle);
+ MojoResult (*Wait)(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline);
+ MojoResult (*WaitMany)(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline);
+ MojoResult (*CreateMessagePipe)(
+ const struct MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1);
+ MojoResult (*WriteMessage)(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags);
+ MojoResult (*ReadMessage)(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags);
+ MojoResult (*CreateDataPipe)(const struct MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle);
+ MojoResult (*WriteData)(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_elements,
+ MojoWriteDataFlags flags);
+ MojoResult (*BeginWriteData)(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoWriteDataFlags flags);
+ MojoResult (*EndWriteData)(MojoHandle data_pipe_producer_handle,
+ uint32_t num_elements_written);
+ MojoResult (*ReadData)(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_elements,
+ MojoReadDataFlags flags);
+ MojoResult (*BeginReadData)(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoReadDataFlags flags);
+ MojoResult (*EndReadData)(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_elements_read);
+ MojoResult (*CreateSharedBuffer)(
+ const struct MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle);
+ MojoResult (*DuplicateBufferHandle)(
+ MojoHandle buffer_handle,
+ const struct MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle);
+ MojoResult (*MapBuffer)(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags);
+ MojoResult (*UnmapBuffer)(void* buffer);
+};
+#pragma pack(pop)
+
+
+#ifdef __cplusplus
+// Intended to be called from the embedder. Returns a |MojoCore| initialized
+// to contain pointers to each of the embedder's MojoCore functions.
+inline MojoSystemThunks MojoMakeSystemThunks() {
+ MojoSystemThunks system_thunks = {
+ sizeof(MojoSystemThunks),
+ MojoGetTimeTicksNow,
+ MojoClose,
+ MojoWait,
+ MojoWaitMany,
+ MojoCreateMessagePipe,
+ MojoWriteMessage,
+ MojoReadMessage,
+ MojoCreateDataPipe,
+ MojoWriteData,
+ MojoBeginWriteData,
+ MojoEndWriteData,
+ MojoReadData,
+ MojoBeginReadData,
+ MojoEndReadData,
+ MojoCreateSharedBuffer,
+ MojoDuplicateBufferHandle,
+ MojoMapBuffer,
+ MojoUnmapBuffer
+ };
+ return system_thunks;
+}
+#endif
+
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+// reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+// "MojoSetSystemThunks"));
+// The expected size of |system_thunks} is returned.
+// The contents of |system_thunks| are copied.
+typedef size_t (*MojoSetSystemThunksFn)(
+ const struct MojoSystemThunks* system_thunks);
+
+#endif // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
diff --git a/mojo/public/platform/native/thunk_export.h b/mojo/public/platform/native/thunk_export.h
new file mode 100644
index 0000000..d118fc5
--- /dev/null
+++ b/mojo/public/platform/native/thunk_export.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_PLATFORM_THUNK_EXPORT_H_
+#define MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_
+
+// Call this function by looking inside the resulting shared object and
+// grabbing the symbol manually.
+//
+// Always export this api.
+#if defined(WIN32)
+#define THUNK_EXPORT __declspec(dllexport)
+#else
+#define THUNK_EXPORT __attribute__((visibility("default")))
+#endif
+
+#endif // MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_
diff --git a/mojo/public/python/BUILD.gn b/mojo/public/python/BUILD.gn
new file mode 100644
index 0000000..66f132f
--- /dev/null
+++ b/mojo/public/python/BUILD.gn
@@ -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.
+
+import("//third_party/cython/rules.gni")
+
+group("python") {
+ deps = [
+ ":base",
+ ":bindings",
+ ":system",
+ ]
+}
+
+# GYP version: mojo.gyp:mojo_python_system
+python_binary_module("system") {
+ python_base_module = "mojo"
+ sources = [
+ "mojo/c_core.pxd",
+ "mojo/c_environment.pxd",
+ "mojo/system.pyx",
+ ]
+ additional_sources = [
+ "src/python_system_helper.cc",
+ "src/python_system_helper.h",
+ ]
+ deps = [
+ "//mojo/public/c/environment",
+ "//mojo/public/c/system:for_shared_library",
+ "//mojo/public/cpp/environment:standalone",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/utility",
+ "//mojo/public/cpp/bindings:callback",
+ ":base",
+ ]
+}
+
+copy("base") {
+ sources = [
+ "mojo/__init__.py",
+ ]
+ outputs = [
+ "$root_out_dir/python/mojo/{{source_file_part}}",
+ ]
+}
+
+# GYP version: mojo.gyp:mojo_python_bindings
+copy("bindings") {
+ sources = [
+ "mojo/bindings/__init__.py",
+ "mojo/bindings/descriptor.py",
+ "mojo/bindings/messaging.py",
+ "mojo/bindings/promise.py",
+ "mojo/bindings/reflection.py",
+ "mojo/bindings/serialization.py",
+ ]
+ outputs = [
+ "$root_out_dir/python/mojo/bindings/{{source_file_part}}",
+ ]
+ deps = [
+ ":base",
+ ":system",
+ ]
+}
diff --git a/mojo/public/python/mojo/__init__.py b/mojo/public/python/mojo/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/mojo/public/python/mojo/__init__.py
@@ -0,0 +1,3 @@
+# 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.
diff --git a/mojo/public/python/mojo/bindings/__init__.py b/mojo/public/python/mojo/bindings/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/__init__.py
@@ -0,0 +1,3 @@
+# 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.
diff --git a/mojo/public/python/mojo/bindings/descriptor.py b/mojo/public/python/mojo/bindings/descriptor.py
new file mode 100644
index 0000000..d56789a
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/descriptor.py
@@ -0,0 +1,555 @@
+# 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.
+
+"""
+The descriptors used to define generated elements of the mojo python bindings.
+"""
+
+import array
+import itertools
+import struct
+
+# pylint: disable=F0401
+import mojo.bindings.serialization as serialization
+import mojo.system
+
+
+class Type(object):
+ """Describes the type of a struct field or a method parameter,"""
+
+ def Convert(self, value): # pylint: disable=R0201
+ """
+ Convert the given value into its canonical representation, raising an
+ exception if the value cannot be converted.
+ """
+ return value
+
+ def GetDefaultValue(self, value):
+ """
+ Returns the default value for this type associated with the given value.
+ This method must be able to correcly handle value being None.
+ """
+ return self.Convert(value)
+
+
+class SerializableType(Type):
+ """Describe a type that can be serialized by itself."""
+
+ def __init__(self, typecode):
+ Type.__init__(self)
+ self.typecode = typecode
+ self.byte_size = struct.calcsize('=%s' % self.GetTypeCode())
+
+ def GetTypeCode(self):
+ """
+ Returns the type code (as defined by the struct module) used to encode
+ this type.
+ """
+ return self.typecode
+
+ def GetByteSize(self):
+ """
+ Returns the size of the encoding of this type.
+ """
+ return self.byte_size
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ """
+ Serialize a value of this type.
+
+ Args:
+ value: the value to serialize.
+ data_offset: the offset to the end of the data bytearray. Used to encode
+ pointers.
+ data: the bytearray to append additional data to.
+ handle_offset: the offset to use to encode handles.
+
+ Returns a a tuple where the first element is the value to encode, and the
+ second is the array of handles to add to the message.
+ """
+ raise NotImplementedError()
+
+ def Deserialize(self, value, data, handles):
+ """
+ Deserialize a value of this type.
+
+ Args:
+ value: the base value for this type. This is always a numeric type, and
+ corresponds to the first element in the tuple returned by
+ Serialize.
+ data: the bytearray to retrieve additional data from.
+ handles: the array of handles contained in the message to deserialize.
+
+ Returns the deserialized value.
+ """
+ raise NotImplementedError()
+
+
+class BooleanType(Type):
+ """Type object for booleans"""
+
+ def Convert(self, value):
+ return bool(value)
+
+
+class NumericType(SerializableType):
+ """Base Type object for all numeric types"""
+
+ def GetDefaultValue(self, value):
+ if value is None:
+ return self.Convert(0)
+ return self.Convert(value)
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ return (value, [])
+
+ def Deserialize(self, value, data, handles):
+ return value
+
+
+class IntegerType(NumericType):
+ """Type object for integer types."""
+
+ def __init__(self, typecode):
+ NumericType.__init__(self, typecode)
+ size = 8 * self.byte_size
+ signed = typecode.islower()
+ if signed:
+ self._min_value = -(1 << (size - 1))
+ self._max_value = (1 << (size - 1)) - 1
+ else:
+ self._min_value = 0
+ self._max_value = (1 << size) - 1
+
+ def Convert(self, value):
+ if value is None:
+ raise TypeError('None is not an integer.')
+ if not isinstance(value, (int, long)):
+ raise TypeError('%r is not an integer type' % value)
+ if value < self._min_value or value > self._max_value:
+ raise OverflowError('%r is not in the range [%d, %d]' %
+ (value, self._min_value, self._max_value))
+ return value
+
+
+class FloatType(NumericType):
+ """Type object for floating point number types."""
+
+ def Convert(self, value):
+ if value is None:
+ raise TypeError('None is not an floating point number.')
+ if not isinstance(value, (int, long, float)):
+ raise TypeError('%r is not a numeric type' % value)
+ return float(value)
+
+
+class PointerType(SerializableType):
+ """Base Type object for pointers."""
+
+ def __init__(self, nullable=False):
+ SerializableType.__init__(self, 'Q')
+ self.nullable = nullable
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ if value is None and not self.nullable:
+ raise serialization.SerializationException(
+ 'Trying to serialize null for non nullable type.')
+ if value is None:
+ return (0, [])
+ return self.SerializePointer(value, data_offset, data, handle_offset)
+
+ def Deserialize(self, value, data, handles):
+ if value == 0:
+ if not self.nullable:
+ raise serialization.DeserializationException(
+ 'Trying to deserialize null for non nullable type.')
+ return None
+ pointed_data = buffer(data, value)
+ (size, nb_elements) = serialization.HEADER_STRUCT.unpack_from(pointed_data)
+ return self.DeserializePointer(size, nb_elements, pointed_data, handles)
+
+ def SerializePointer(self, value, data_offset, data, handle_offset):
+ """Serialize the not null value."""
+ raise NotImplementedError()
+
+ def DeserializePointer(self, size, nb_elements, data, handles):
+ raise NotImplementedError()
+
+
+class StringType(PointerType):
+ """
+ Type object for strings.
+
+ Strings are represented as unicode, and the conversion is done using the
+ default encoding if a string instance is used.
+ """
+
+ def __init__(self, nullable=False):
+ PointerType.__init__(self, nullable)
+ self._array_type = NativeArrayType('B', nullable)
+
+ def Convert(self, value):
+ if value is None or isinstance(value, unicode):
+ return value
+ if isinstance(value, str):
+ return unicode(value)
+ raise TypeError('%r is not a string' % value)
+
+ def SerializePointer(self, value, data_offset, data, handle_offset):
+ string_array = array.array('b')
+ string_array.fromstring(value.encode('utf8'))
+ return self._array_type.SerializeArray(
+ string_array, data_offset, data, handle_offset)
+
+ def DeserializePointer(self, size, nb_elements, data, handles):
+ string_array = self._array_type.DeserializeArray(
+ size, nb_elements, data, handles)
+ return unicode(string_array.tostring(), 'utf8')
+
+
+class HandleType(SerializableType):
+ """Type object for handles."""
+
+ def __init__(self, nullable=False):
+ SerializableType.__init__(self, 'i')
+ self.nullable = nullable
+
+ def Convert(self, value):
+ if value is None:
+ return mojo.system.Handle()
+ if not isinstance(value, mojo.system.Handle):
+ raise TypeError('%r is not a handle' % value)
+ return value
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ if not value.IsValid() and not self.nullable:
+ raise serialization.SerializationException(
+ 'Trying to serialize null for non nullable type.')
+ if not value.IsValid():
+ return (-1, [])
+ return (handle_offset, [value])
+
+ def Deserialize(self, value, data, handles):
+ if value == -1:
+ if not self.nullable:
+ raise serialization.DeserializationException(
+ 'Trying to deserialize null for non nullable type.')
+ return mojo.system.Handle()
+ # TODO(qsr) validate handle order
+ return handles[value]
+
+
+class BaseArrayType(PointerType):
+ """Abstract Type object for arrays."""
+
+ def __init__(self, nullable=False, length=0):
+ PointerType.__init__(self, nullable)
+ self.length = length
+
+ def SerializePointer(self, value, data_offset, data, handle_offset):
+ if self.length != 0 and len(value) != self.length:
+ raise serialization.SerializationException('Incorrect array size')
+ return self.SerializeArray(value, data_offset, data, handle_offset)
+
+ def SerializeArray(self, value, data_offset, data, handle_offset):
+ """Serialize the not null array."""
+ raise NotImplementedError()
+
+ def DeserializePointer(self, size, nb_elements, data, handles):
+ if self.length != 0 and size != self.length:
+ raise serialization.DeserializationException('Incorrect array size')
+ return self.DeserializeArray(size, nb_elements, data, handles)
+
+ def DeserializeArray(self, size, nb_elements, data, handles):
+ raise NotImplementedError()
+
+
+class BooleanArrayType(BaseArrayType):
+
+ def __init__(self, nullable=False, length=0):
+ BaseArrayType.__init__(self, nullable, length)
+ self._array_type = NativeArrayType('B', nullable)
+
+ def Convert(self, value):
+ if value is None:
+ return value
+ return [TYPE_BOOL.Convert(x) for x in value]
+
+ def SerializeArray(self, value, data_offset, data, handle_offset):
+ groups = [value[i:i+8] for i in range(0, len(value), 8)]
+ converted = array.array('B', [_ConvertBooleansToByte(x) for x in groups])
+ return _SerializeNativeArray(converted, data_offset, data, len(value))
+
+ def DeserializeArray(self, size, nb_elements, data, handles):
+ converted = self._array_type.DeserializeArray(
+ size, nb_elements, data, handles)
+ elements = list(itertools.islice(
+ itertools.chain.from_iterable(
+ [_ConvertByteToBooleans(x, 8) for x in converted]),
+ 0,
+ nb_elements))
+ return elements
+
+
+class GenericArrayType(BaseArrayType):
+ """Type object for arrays of pointers."""
+
+ def __init__(self, sub_type, nullable=False, length=0):
+ BaseArrayType.__init__(self, nullable, length)
+ assert isinstance(sub_type, SerializableType)
+ self.sub_type = sub_type
+
+ def Convert(self, value):
+ if value is None:
+ return value
+ return [self.sub_type.Convert(x) for x in value]
+
+ def SerializeArray(self, value, data_offset, data, handle_offset):
+ size = (serialization.HEADER_STRUCT.size +
+ self.sub_type.GetByteSize() * len(value))
+ data_end = len(data)
+ position = len(data) + serialization.HEADER_STRUCT.size
+ data.extend(bytearray(size +
+ serialization.NeededPaddingForAlignment(size)))
+ returned_handles = []
+ to_pack = []
+ for item in value:
+ (new_data, new_handles) = self.sub_type.Serialize(
+ item,
+ len(data) - position,
+ data,
+ handle_offset + len(returned_handles))
+ to_pack.append(new_data)
+ returned_handles.extend(new_handles)
+ position = position + self.sub_type.GetByteSize()
+ serialization.HEADER_STRUCT.pack_into(data, data_end, size, len(value))
+ struct.pack_into('%d%s' % (len(value), self.sub_type.GetTypeCode()),
+ data,
+ data_end + serialization.HEADER_STRUCT.size,
+ *to_pack)
+ return (data_offset, returned_handles)
+
+ def DeserializeArray(self, size, nb_elements, data, handles):
+ values = struct.unpack_from(
+ '%d%s' % (nb_elements, self.sub_type.GetTypeCode()),
+ buffer(data, serialization.HEADER_STRUCT.size))
+ result = []
+ position = serialization.HEADER_STRUCT.size
+ for value in values:
+ result.append(
+ self.sub_type.Deserialize(value, buffer(data, position), handles))
+ position += self.sub_type.GetByteSize()
+ return result
+
+
+class NativeArrayType(BaseArrayType):
+ """Type object for arrays of native types."""
+
+ def __init__(self, typecode, nullable=False, length=0):
+ BaseArrayType.__init__(self, nullable, length)
+ self.array_typecode = typecode
+
+ def Convert(self, value):
+ if value is None:
+ return value
+ if (isinstance(value, array.array) and
+ value.array_typecode == self.array_typecode):
+ return value
+ return array.array(self.array_typecode, value)
+
+ def SerializeArray(self, value, data_offset, data, handle_offset):
+ return _SerializeNativeArray(value, data_offset, data, len(value))
+
+ def DeserializeArray(self, size, nb_elements, data, handles):
+ result = array.array(self.array_typecode)
+ result.fromstring(buffer(data,
+ serialization.HEADER_STRUCT.size,
+ size - serialization.HEADER_STRUCT.size))
+ return result
+
+
+class StructType(PointerType):
+ """Type object for structs."""
+
+ def __init__(self, struct_type, nullable=False):
+ PointerType.__init__(self)
+ self.struct_type = struct_type
+ self.nullable = nullable
+
+ def Convert(self, value):
+ if value is None or isinstance(value, self.struct_type):
+ return value
+ raise TypeError('%r is not an instance of %r' % (value, self.struct_type))
+
+ def GetDefaultValue(self, value):
+ if value:
+ return self.struct_type()
+ return None
+
+ def SerializePointer(self, value, data_offset, data, handle_offset):
+ (new_data, new_handles) = value.Serialize(handle_offset)
+ data.extend(new_data)
+ return (data_offset, new_handles)
+
+ def DeserializePointer(self, size, nb_elements, data, handles):
+ return self.struct_type.Deserialize(data, handles)
+
+
+class NoneType(SerializableType):
+ """Placeholder type, used temporarily until all mojo types are handled."""
+
+ def __init__(self):
+ SerializableType.__init__(self, 'B')
+
+ def Convert(self, value):
+ return None
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ return (0, [])
+
+ def Deserialize(self, value, data, handles):
+ return None
+
+
+TYPE_NONE = NoneType()
+
+TYPE_BOOL = BooleanType()
+
+TYPE_INT8 = IntegerType('b')
+TYPE_INT16 = IntegerType('h')
+TYPE_INT32 = IntegerType('i')
+TYPE_INT64 = IntegerType('q')
+
+TYPE_UINT8 = IntegerType('B')
+TYPE_UINT16 = IntegerType('H')
+TYPE_UINT32 = IntegerType('I')
+TYPE_UINT64 = IntegerType('Q')
+
+TYPE_FLOAT = FloatType('f')
+TYPE_DOUBLE = FloatType('d')
+
+TYPE_STRING = StringType()
+TYPE_NULLABLE_STRING = StringType(True)
+
+TYPE_HANDLE = HandleType()
+TYPE_NULLABLE_HANDLE = HandleType(True)
+
+
+class FieldDescriptor(object):
+ """Describes a field in a generated struct."""
+
+ def __init__(self, name, field_type, index, version, default_value=None):
+ self.name = name
+ self.field_type = field_type
+ self.version = version
+ self.index = index
+ self._default_value = default_value
+
+ def GetDefaultValue(self):
+ return self.field_type.GetDefaultValue(self._default_value)
+
+
+class FieldGroup(object):
+ """
+ Describe a list of field in the generated struct that must be
+ serialized/deserialized together.
+ """
+ def __init__(self, descriptors):
+ self.descriptors = descriptors
+
+ def GetDescriptors(self):
+ return self.descriptors
+
+ def GetTypeCode(self):
+ raise NotImplementedError()
+
+ def GetByteSize(self):
+ raise NotImplementedError()
+
+ def GetVersion(self):
+ raise NotImplementedError()
+
+ def Serialize(self, obj, data_offset, data, handle_offset):
+ raise NotImplementedError()
+
+ def Deserialize(self, value, data, handles):
+ raise NotImplementedError()
+
+
+class SingleFieldGroup(FieldGroup, FieldDescriptor):
+ """A FieldGroup that contains a single FieldDescriptor."""
+
+ def __init__(self, name, field_type, index, version, default_value=None):
+ FieldDescriptor.__init__(
+ self, name, field_type, index, version, default_value)
+ FieldGroup.__init__(self, [self])
+
+ def GetTypeCode(self):
+ return self.field_type.GetTypeCode()
+
+ def GetByteSize(self):
+ return self.field_type.GetByteSize()
+
+ def GetVersion(self):
+ return self.version
+
+ def Serialize(self, obj, data_offset, data, handle_offset):
+ value = getattr(obj, self.name)
+ return self.field_type.Serialize(value, data_offset, data, handle_offset)
+
+ def Deserialize(self, value, data, handles):
+ entity = self.field_type.Deserialize(value, data, handles)
+ return { self.name: entity }
+
+
+class BooleanGroup(FieldGroup):
+ """A FieldGroup to pack booleans."""
+ def __init__(self, descriptors):
+ FieldGroup.__init__(self, descriptors)
+ self.version = min([descriptor.version for descriptor in descriptors])
+
+ def GetTypeCode(self):
+ return 'B'
+
+ def GetByteSize(self):
+ return 1
+
+ def GetVersion(self):
+ return self.version
+
+ def Serialize(self, obj, data_offset, data, handle_offset):
+ value = _ConvertBooleansToByte(
+ [getattr(obj, field.name) for field in self.GetDescriptors()])
+ return (value, [])
+
+ def Deserialize(self, value, data, handles):
+ values = itertools.izip_longest([x.name for x in self.descriptors],
+ _ConvertByteToBooleans(value),
+ fillvalue=False)
+ return dict(values)
+
+
+def _SerializeNativeArray(value, data_offset, data, length):
+ data_size = len(data)
+ data.extend(bytearray(serialization.HEADER_STRUCT.size))
+ data.extend(buffer(value))
+ data_length = len(data) - data_size
+ data.extend(bytearray(serialization.NeededPaddingForAlignment(data_length)))
+ serialization.HEADER_STRUCT.pack_into(data, data_size, data_length, length)
+ return (data_offset, [])
+
+
+def _ConvertBooleansToByte(booleans):
+ """Pack a list of booleans into an integer."""
+ return reduce(lambda x, y: x * 2 + y, reversed(booleans), 0)
+
+
+def _ConvertByteToBooleans(value, min_size=0):
+ "Unpack an integer into a list of booleans."""
+ res = []
+ while value:
+ res.append(bool(value&1))
+ value = value / 2
+ res.extend([False] * (min_size - len(res)))
+ return res
diff --git a/mojo/public/python/mojo/bindings/messaging.py b/mojo/public/python/mojo/bindings/messaging.py
new file mode 100644
index 0000000..956f5b3
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/messaging.py
@@ -0,0 +1,387 @@
+# 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.
+
+"""Utility classes to handle sending and receiving messages."""
+
+
+import struct
+import weakref
+
+# pylint: disable=F0401
+import mojo.bindings.serialization as serialization
+import mojo.system as system
+
+
+# The flag values for a message header.
+NO_FLAG = 0
+MESSAGE_EXPECTS_RESPONSE_FLAG = 1 << 0
+MESSAGE_IS_RESPONSE_FLAG = 1 << 1
+
+
+class MessageHeader(object):
+ """The header of a mojo message."""
+
+ _SIMPLE_MESSAGE_NUM_FIELDS = 2
+ _SIMPLE_MESSAGE_STRUCT = struct.Struct("=IIII")
+
+ _REQUEST_ID_STRUCT = struct.Struct("=Q")
+ _REQUEST_ID_OFFSET = _SIMPLE_MESSAGE_STRUCT.size
+
+ _MESSAGE_WITH_REQUEST_ID_NUM_FIELDS = 3
+ _MESSAGE_WITH_REQUEST_ID_SIZE = (
+ _SIMPLE_MESSAGE_STRUCT.size + _REQUEST_ID_STRUCT.size)
+
+ def __init__(self, message_type, flags, request_id=0, data=None):
+ self._message_type = message_type
+ self._flags = flags
+ self._request_id = request_id
+ self._data = data
+
+ @classmethod
+ def Deserialize(cls, data):
+ buf = buffer(data)
+ if len(data) < cls._SIMPLE_MESSAGE_STRUCT.size:
+ raise serialization.DeserializationException('Header is too short.')
+ (size, version, message_type, flags) = (
+ cls._SIMPLE_MESSAGE_STRUCT.unpack_from(buf))
+ if (version < cls._SIMPLE_MESSAGE_NUM_FIELDS):
+ raise serialization.DeserializationException('Incorrect version.')
+ request_id = 0
+ if _HasRequestId(flags):
+ if version < cls._MESSAGE_WITH_REQUEST_ID_NUM_FIELDS:
+ raise serialization.DeserializationException('Incorrect version.')
+ if (size < cls._MESSAGE_WITH_REQUEST_ID_SIZE or
+ len(data) < cls._MESSAGE_WITH_REQUEST_ID_SIZE):
+ raise serialization.DeserializationException('Header is too short.')
+ (request_id, ) = cls._REQUEST_ID_STRUCT.unpack_from(
+ buf, cls._REQUEST_ID_OFFSET)
+ return MessageHeader(message_type, flags, request_id, data)
+
+ @property
+ def message_type(self):
+ return self._message_type
+
+ # pylint: disable=E0202
+ @property
+ def request_id(self):
+ assert self.has_request_id
+ return self._request_id
+
+ # pylint: disable=E0202
+ @request_id.setter
+ def request_id(self, request_id):
+ assert self.has_request_id
+ self._request_id = request_id
+ self._REQUEST_ID_STRUCT.pack_into(self._data, self._REQUEST_ID_OFFSET,
+ request_id)
+
+ @property
+ def has_request_id(self):
+ return _HasRequestId(self._flags)
+
+ @property
+ def expects_response(self):
+ return self._HasFlag(MESSAGE_EXPECTS_RESPONSE_FLAG)
+
+ @property
+ def is_response(self):
+ return self._HasFlag(MESSAGE_IS_RESPONSE_FLAG)
+
+ @property
+ def size(self):
+ if self.has_request_id:
+ return self._MESSAGE_WITH_REQUEST_ID_SIZE
+ return self._SIMPLE_MESSAGE_STRUCT.size
+
+ def Serialize(self):
+ if not self._data:
+ self._data = bytearray(self.size)
+ version = self._SIMPLE_MESSAGE_NUM_FIELDS
+ size = self._SIMPLE_MESSAGE_STRUCT.size
+ if self.has_request_id:
+ version = self._MESSAGE_WITH_REQUEST_ID_NUM_FIELDS
+ size = self._MESSAGE_WITH_REQUEST_ID_SIZE
+ self._SIMPLE_MESSAGE_STRUCT.pack_into(self._data, 0, size, version,
+ self._message_type, self._flags)
+ if self.has_request_id:
+ self._REQUEST_ID_STRUCT.pack_into(self._data, self._REQUEST_ID_OFFSET,
+ self._request_id)
+ return self._data
+
+ def _HasFlag(self, flag):
+ return self._flags & flag != 0
+
+
+class Message(object):
+ """A message for a message pipe. This contains data and handles."""
+
+ def __init__(self, data=None, handles=None):
+ self.data = data
+ self.handles = handles
+ self._header = None
+ self._payload = None
+
+ @property
+ def header(self):
+ if self._header is None:
+ self._header = MessageHeader.Deserialize(self.data)
+ return self._header
+
+ @property
+ def payload(self):
+ if self._payload is None:
+ self._payload = Message(self.data[self.header.size:], self.handles)
+ return self._payload
+
+ def SetRequestId(self, request_id):
+ header = self.header
+ header.request_id = request_id
+ (data, _) = header.Serialize()
+ self.data[:header.Size] = data[:header.Size]
+
+
+class MessageReceiver(object):
+ """A class which implements this interface can receive Message objects."""
+
+ def Accept(self, message):
+ """
+ Receive a Message. The MessageReceiver is allowed to mutate the message.
+
+ Args:
+ message: the received message.
+
+ Returns:
+ True if the message has been handled, False otherwise.
+ """
+ raise NotImplementedError()
+
+
+class MessageReceiverWithResponder(MessageReceiver):
+ """
+ A MessageReceiver that can also handle the response message generated from the
+ given message.
+ """
+
+ def AcceptWithResponder(self, message, responder):
+ """
+ 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 as part of the call to
+ AcceptWithResponder, or some time after its return.
+
+ Args:
+ message: the received message.
+ responder: the responder that will receive the response.
+
+ Returns:
+ True if the message has been handled, False otherwise.
+ """
+ raise NotImplementedError()
+
+
+class ConnectionErrorHandler(object):
+ """
+ A ConnectionErrorHandler is notified of an error happening while using the
+ bindings over message pipes.
+ """
+
+ def OnError(self, result):
+ raise NotImplementedError()
+
+
+class Connector(MessageReceiver):
+ """
+ A Connector owns a message pipe and will send any received messages to the
+ registered MessageReceiver. It also acts as a MessageReceiver and will send
+ any message through the handle.
+
+ The method Start must be called before the Connector will start listening to
+ incoming messages.
+ """
+
+ def __init__(self, handle):
+ MessageReceiver.__init__(self)
+ self._handle = handle
+ self._cancellable = None
+ self._incoming_message_receiver = None
+ self._error_handler = None
+
+ def __del__(self):
+ if self._cancellable:
+ self._cancellable()
+
+ def SetIncomingMessageReceiver(self, message_receiver):
+ """
+ Set the MessageReceiver that will receive message from the owned message
+ pipe.
+ """
+ self._incoming_message_receiver = message_receiver
+
+ def SetErrorHandler(self, error_handler):
+ """
+ Set the ConnectionErrorHandler that will be notified of errors on the owned
+ message pipe.
+ """
+ self._error_handler = error_handler
+
+ def Start(self):
+ assert not self._cancellable
+ self._RegisterAsyncWaiterForRead()
+
+ def Accept(self, message):
+ result = self._handle.WriteMessage(message.data, message.handles)
+ return result == system.RESULT_OK
+
+ def Close(self):
+ if self._cancellable:
+ self._cancellable()
+ self._cancellable = None
+ self._handle.Close()
+
+ def _OnAsyncWaiterResult(self, result):
+ self._cancellable = None
+ if result == system.RESULT_OK:
+ self._ReadOutstandingMessages()
+ else:
+ self._OnError(result)
+
+ def _OnError(self, result):
+ assert not self._cancellable
+ if self._error_handler:
+ self._error_handler.OnError(result)
+
+ def _RegisterAsyncWaiterForRead(self) :
+ assert not self._cancellable
+ self._cancellable = self._handle.AsyncWait(
+ system.HANDLE_SIGNAL_READABLE,
+ system.DEADLINE_INDEFINITE,
+ _WeakCallback(self._OnAsyncWaiterResult))
+
+ def _ReadOutstandingMessages(self):
+ result = system.RESULT_OK
+ while result == system.RESULT_OK:
+ result = _ReadAndDispatchMessage(self._handle,
+ self._incoming_message_receiver)
+ if result == system.RESULT_SHOULD_WAIT:
+ self._RegisterAsyncWaiterForRead()
+ return
+ self._OnError(result)
+
+
+class Router(MessageReceiverWithResponder):
+ """
+ A Router will handle mojo message and forward those to a Connector. It deals
+ with parsing of headers and adding of request ids in order to be able to match
+ a response to a request.
+ """
+
+ def __init__(self, handle):
+ MessageReceiverWithResponder.__init__(self)
+ self._incoming_message_receiver = None
+ self._next_request_id = 1
+ self._responders = {}
+ self._connector = Connector(handle)
+ self._connector.SetIncomingMessageReceiver(
+ ForwardingMessageReceiver(self._HandleIncomingMessage))
+
+ def Start(self):
+ self._connector.Start()
+
+ def SetIncomingMessageReceiver(self, message_receiver):
+ """
+ Set the MessageReceiver that will receive message from the owned message
+ pipe.
+ """
+ self._incoming_message_receiver = message_receiver
+
+ def SetErrorHandler(self, error_handler):
+ """
+ Set the ConnectionErrorHandler that will be notified of errors on the owned
+ message pipe.
+ """
+ self._connector.SetErrorHandler(error_handler)
+
+ def Accept(self, message):
+ # A message without responder is directly forwarded to the connector.
+ return self._connector.Accept(message)
+
+ def AcceptWithResponder(self, message, responder):
+ # The message must have a header.
+ header = message.header
+ assert header.expects_response
+ request_id = self.NextRequestId()
+ header.request_id = request_id
+ if not self._connector.Accept(message):
+ return False
+ self._responders[request_id] = responder
+ return True
+
+ def Close(self):
+ self._connector.Close()
+
+ def _HandleIncomingMessage(self, message):
+ header = message.header
+ if header.expects_response:
+ if self._incoming_message_receiver:
+ return self._incoming_message_receiver.AcceptWithResponder(
+ message, self)
+ # If we receive a request expecting a response when the client is not
+ # listening, then we have no choice but to tear down the pipe.
+ self.Close()
+ return False
+ if header.is_response:
+ request_id = header.request_id
+ responder = self._responders.pop(request_id, None)
+ if responder is None:
+ return False
+ return responder.Accept(message)
+ if self._incoming_message_receiver:
+ return self._incoming_message_receiver.Accept(message)
+ # Ok to drop the message
+ return False
+
+ def NextRequestId(self):
+ request_id = self._next_request_id
+ while request_id == 0 or request_id in self._responders:
+ request_id = (request_id + 1) % (1 << 64)
+ self._next_request_id = (request_id + 1) % (1 << 64)
+ return request_id
+
+class ForwardingMessageReceiver(MessageReceiver):
+ """A MessageReceiver that forward calls to |Accept| to a callable."""
+
+ def __init__(self, callback):
+ MessageReceiver.__init__(self)
+ self._callback = callback
+
+ def Accept(self, message):
+ return self._callback(message)
+
+
+def _WeakCallback(callback):
+ func = callback.im_func
+ self = callback.im_self
+ if not self:
+ return callback
+ weak_self = weakref.ref(self)
+ def Callback(*args, **kwargs):
+ self = weak_self()
+ if self:
+ return func(self, *args, **kwargs)
+ return Callback
+
+
+def _ReadAndDispatchMessage(handle, message_receiver):
+ (result, _, sizes) = handle.ReadMessage()
+ if result == system.RESULT_OK and message_receiver:
+ message_receiver.Accept(Message(bytearray(), []))
+ if result != system.RESULT_RESOURCE_EXHAUSTED:
+ return result
+ (result, data, _) = handle.ReadMessage(bytearray(sizes[0]))
+ if result == system.RESULT_OK and message_receiver:
+ message_receiver.Accept(Message(data[0], data[1]))
+ return result
+
+def _HasRequestId(flags):
+ return flags & (MESSAGE_EXPECTS_RESPONSE_FLAG|MESSAGE_IS_RESPONSE_FLAG) != 0
diff --git a/mojo/public/python/mojo/bindings/promise.py b/mojo/public/python/mojo/bindings/promise.py
new file mode 100644
index 0000000..c5d7d7c
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/promise.py
@@ -0,0 +1,188 @@
+# 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.
+
+"""
+Promise used by the python bindings.
+
+The API is following the ECMAScript 6 API for promises.
+"""
+
+
+class Promise(object):
+ """The promise object."""
+
+ STATE_PENDING = 0
+ STATE_FULLFILLED = 1
+ STATE_REJECTED = 2
+ STATE_BOUND = 3
+
+ def __init__(self, generator_function):
+ """
+ Constructor.
+
+ Args:
+ generator_function: A function taking 2 arguments: resolve and reject.
+ When |resolve| is called, the promise is fullfilled with the given value.
+ When |reject| is called, the promise is rejected with the given value.
+ A promise can only be resolved or rejected once, all following calls will
+ have no effect.
+ """
+ self._onCatched = []
+ self._onFulfilled = []
+ self._onRejected = []
+ self._state = Promise.STATE_PENDING
+ self._result = None
+ if generator_function:
+ generator_function(self._Resolve, self._Reject)
+
+ @staticmethod
+ def Resolve(value):
+ """
+ If value is a promise, make a promise that have the same behavior as value,
+ otherwise make a promise that fulfills to value.
+ """
+ if isinstance(value, Promise):
+ return value
+ return Promise(lambda x, y: x(value))
+
+ @staticmethod
+ def Reject(reason):
+ "Make a promise that rejects to reason."""
+ return Promise(lambda x, y: y(reason))
+
+ @staticmethod
+ def All(*iterable):
+ """
+ Make a promise that fulfills when every item in the array fulfills, and
+ rejects if (and when) any item rejects. Each array item is passed to
+ Promise.resolve, so the array can be a mixture of promise-like objects and
+ other objects. The fulfillment value is an array (in order) of fulfillment
+ values. The rejection value is the first rejection value.
+ """
+ def GeneratorFunction(resolve, reject):
+ state = {
+ 'rejected': False,
+ 'nb_resolved': 0,
+ }
+ promises = [Promise.Resolve(x) for x in iterable]
+ results = [None for x in promises]
+ def OnFullfilled(i):
+ def OnFullfilled(res):
+ if state['rejected']:
+ return
+ results[i] = res
+ state['nb_resolved'] = state['nb_resolved'] + 1
+ if state['nb_resolved'] == len(results):
+ resolve(results)
+ return OnFullfilled
+ def OnRejected(reason):
+ if state['rejected']:
+ return
+ state['rejected'] = True
+ reject(reason)
+
+ for (i, promise) in enumerate(promises):
+ promise.Then(OnFullfilled(i), OnRejected)
+ return Promise(GeneratorFunction)
+
+ @staticmethod
+ def Race(*iterable):
+ """
+ Make a Promise that fulfills as soon as any item fulfills, or rejects as
+ soon as any item rejects, whichever happens first.
+ """
+ def GeneratorFunction(resolve, reject):
+ state = {
+ 'ended': False
+ }
+ def OnEvent(callback):
+ def OnEvent(res):
+ if state['ended']:
+ return
+ state['ended'] = True
+ callback(res)
+ return OnEvent
+ for promise in [Promise.Resolve(x) for x in iterable]:
+ promise.Then(OnEvent(resolve), OnEvent(reject))
+ return Promise(GeneratorFunction)
+
+ @property
+ def state(self):
+ if isinstance(self._result, Promise):
+ return self._result.state
+ return self._state
+
+ def Then(self, onFullfilled=None, onRejected=None):
+ """
+ onFulfilled is called when/if this promise resolves. onRejected is called
+ when/if this promise rejects. Both are optional, if either/both are omitted
+ the next onFulfilled/onRejected in the chain is called. Both callbacks have
+ a single parameter, the fulfillment value or rejection reason. |Then|
+ returns a new promise equivalent to the value you return from
+ onFulfilled/onRejected after being passed through Resolve. If an
+ error is thrown in the callback, the returned promise rejects with that
+ error.
+ """
+ if isinstance(self._result, Promise):
+ return self._result.Then(onFullfilled, onRejected)
+ def GeneratorFunction(resolve, reject):
+ if self._state == Promise.STATE_PENDING:
+ self._onFulfilled.append(_Delegate(resolve, reject, onFullfilled))
+ self._onRejected.append(_Delegate(reject, reject, onRejected))
+ if self._state == self.STATE_FULLFILLED:
+ _Delegate(resolve, reject, onFullfilled)(self._result)
+ if self._state == self.STATE_REJECTED:
+ recover = reject
+ if onRejected:
+ recover = resolve
+ _Delegate(recover, reject, onRejected)(self._result)
+ return Promise(GeneratorFunction)
+
+ def Catch(self, onCatched):
+ """Equivalent to |Then(None, onCatched)|"""
+ return self.Then(None, onCatched)
+
+ def _Resolve(self, value):
+ if self.state != Promise.STATE_PENDING:
+ return
+ self._result = value
+ if isinstance(value, Promise):
+ self._state = Promise.STATE_BOUND
+ self._result.Then(_IterateAction(self._onFulfilled),
+ _IterateAction(self._onRejected))
+ return
+ self._state = Promise.STATE_FULLFILLED
+ for f in self._onFulfilled:
+ f(value)
+ self._onFulfilled = None
+ self._onRejected = None
+
+ def _Reject(self, reason):
+ if self.state != Promise.STATE_PENDING:
+ return
+ self._result = reason
+ self._state = Promise.STATE_REJECTED
+ for f in self._onRejected:
+ f(reason)
+ self._onFulfilled = None
+ self._onRejected = None
+
+
+def _IterateAction(iterable):
+ def _Run(x):
+ for f in iterable:
+ f(x)
+ return _Run
+
+
+def _Delegate(resolve, reject, action):
+ def _Run(x):
+ try:
+ if action:
+ resolve(action(x))
+ else:
+ resolve(x)
+ except Exception as e:
+ reject(e)
+ return _Run
diff --git a/mojo/public/python/mojo/bindings/reflection.py b/mojo/public/python/mojo/bindings/reflection.py
new file mode 100644
index 0000000..c682e5e
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/reflection.py
@@ -0,0 +1,163 @@
+# 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.
+
+"""The metaclasses used by the mojo python bindings."""
+
+import itertools
+
+# pylint: disable=F0401
+import mojo.bindings.serialization as serialization
+
+
+class MojoEnumType(type):
+ """Meta class for enumerations.
+
+ Usage:
+ class MyEnum(object):
+ __metaclass__ = MojoEnumType
+ VALUES = [
+ ('A', 0),
+ 'B',
+ ('C', 5),
+ ]
+
+ This will define a enum with 3 values, 'A' = 0, 'B' = 1 and 'C' = 5.
+ """
+
+ def __new__(mcs, name, bases, dictionary):
+ dictionary['__slots__'] = ()
+ dictionary['__new__'] = None
+ for value in dictionary.pop('VALUES', []):
+ if not isinstance(value, tuple):
+ raise ValueError('incorrect value: %r' % value)
+ key, enum_value = value
+ if isinstance(key, str) and isinstance(enum_value, int):
+ dictionary[key] = enum_value
+ else:
+ raise ValueError('incorrect value: %r' % value)
+ return type.__new__(mcs, name, bases, dictionary)
+
+ def __setattr__(mcs, key, value):
+ raise AttributeError, 'can\'t set attribute'
+
+ def __delattr__(mcs, key):
+ raise AttributeError, 'can\'t delete attribute'
+
+
+class MojoStructType(type):
+ """Meta class for structs.
+
+ Usage:
+ class MyStruct(object):
+ __metaclass__ = MojoStructType
+ DESCRIPTOR = {
+ 'constants': {
+ 'C1': 1,
+ 'C2': 2,
+ },
+ 'enums': {
+ 'ENUM1': [
+ ('V1', 1),
+ 'V2',
+ ],
+ 'ENUM2': [
+ ('V1', 1),
+ 'V2',
+ ],
+ },
+ 'fields': [
+ FieldDescriptor('x', _descriptor.TYPE_INT32, 0),
+ ],
+ }
+
+ This will define an struct, with:
+ - 2 constants 'C1' and 'C2';
+ - 2 enums 'ENUM1' and 'ENUM2', each of those having 2 values, 'V1' and
+ 'V2';
+ - 1 int32 field named 'x'.
+ """
+
+ def __new__(mcs, name, bases, dictionary):
+ dictionary['__slots__'] = ('_fields')
+ descriptor = dictionary.pop('DESCRIPTOR', {})
+
+ # Add constants
+ dictionary.update(descriptor.get('constants', {}))
+
+ # Add enums
+ enums = descriptor.get('enums', {})
+ for key in enums:
+ dictionary[key] = MojoEnumType(key, (object,), { 'VALUES': enums[key] })
+
+ # Add fields
+ groups = descriptor.get('fields', [])
+
+ fields = list(
+ itertools.chain.from_iterable([group.descriptors for group in groups]))
+ fields.sort(key=lambda f: f.index)
+ for field in fields:
+ dictionary[field.name] = _BuildProperty(field)
+
+ # Add init
+ dictionary['__init__'] = _StructInit(fields)
+
+ # Add serialization method
+ serialization_object = serialization.Serialization(groups)
+ def Serialize(self, handle_offset=0):
+ return serialization_object.Serialize(self, handle_offset)
+ dictionary['Serialize'] = Serialize
+
+ def Deserialize(cls, data, handles):
+ result = cls.__new__(cls)
+ fields = {}
+ serialization_object.Deserialize(fields, data, handles)
+ result._fields = fields
+ return result
+ dictionary['Deserialize'] = classmethod(Deserialize)
+
+ return type.__new__(mcs, name, bases, dictionary)
+
+ # Prevent adding new attributes, or mutating constants.
+ def __setattr__(mcs, key, value):
+ raise AttributeError, 'can\'t set attribute'
+
+ # Prevent deleting constants.
+ def __delattr__(mcs, key):
+ raise AttributeError, 'can\'t delete attribute'
+
+
+def _StructInit(fields):
+ def _Init(self, *args, **kwargs):
+ if len(args) + len(kwargs) > len(fields):
+ raise TypeError('__init__() takes %d argument (%d given)' %
+ (len(fields), len(args) + len(kwargs)))
+ self._fields = {}
+ for f, a in zip(fields, args):
+ self.__setattr__(f.name, a)
+ remaining_fields = set(x.name for x in fields[len(args):])
+ for name in kwargs:
+ if not name in remaining_fields:
+ if name in (x.name for x in fields[:len(args)]):
+ raise TypeError(
+ '__init__() got multiple values for keyword argument %r' % name)
+ raise TypeError('__init__() got an unexpected keyword argument %r' %
+ name)
+ self.__setattr__(name, kwargs[name])
+ return _Init
+
+
+def _BuildProperty(field):
+ """Build the property for the given field."""
+
+ # pylint: disable=W0212
+ def Get(self):
+ if field.name not in self._fields:
+ self._fields[field.name] = field.GetDefaultValue()
+ return self._fields[field.name]
+
+ # pylint: disable=W0212
+ def Set(self, value):
+ self._fields[field.name] = field.field_type.Convert(value)
+
+ return property(Get, Set)
diff --git a/mojo/public/python/mojo/bindings/serialization.py b/mojo/public/python/mojo/bindings/serialization.py
new file mode 100644
index 0000000..9a4b6a9
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/serialization.py
@@ -0,0 +1,126 @@
+# 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.
+
+"""Utility classes for serialization"""
+
+import struct
+
+
+# Format of a header for a struct or an array.
+HEADER_STRUCT = struct.Struct("=II")
+
+
+class SerializationException(Exception):
+ """Error when strying to serialize a struct."""
+ pass
+
+
+class DeserializationException(Exception):
+ """Error when strying to deserialize a struct."""
+ pass
+
+
+class Serialization(object):
+ """
+ Helper class to serialize/deserialize a struct.
+ """
+ def __init__(self, groups):
+ self.version = _GetVersion(groups)
+ self._groups = groups
+ main_struct = _GetStruct(groups)
+ self.size = HEADER_STRUCT.size + main_struct.size
+ self._struct_per_version = {
+ self.version: main_struct,
+ }
+ self._groups_per_version = {
+ self.version: groups,
+ }
+
+ def _GetMainStruct(self):
+ return self._GetStruct(self.version)
+
+ def _GetGroups(self, version):
+ # If asking for a version greater than the last known.
+ version = min(version, self.version)
+ if version not in self._groups_per_version:
+ self._groups_per_version[version] = _FilterGroups(self._groups, version)
+ return self._groups_per_version[version]
+
+ def _GetStruct(self, version):
+ # If asking for a version greater than the last known.
+ version = min(version, self.version)
+ if version not in self._struct_per_version:
+ self._struct_per_version[version] = _GetStruct(self._GetGroups(version))
+ return self._struct_per_version[version]
+
+ def Serialize(self, obj, handle_offset):
+ """
+ Serialize the given obj. handle_offset is the the first value to use when
+ encoding handles.
+ """
+ handles = []
+ data = bytearray(self.size)
+ HEADER_STRUCT.pack_into(data, 0, self.size, self.version)
+ position = HEADER_STRUCT.size
+ to_pack = []
+ for group in self._groups:
+ position = position + NeededPaddingForAlignment(position,
+ group.GetByteSize())
+ (entry, new_handles) = group.Serialize(
+ obj,
+ len(data) - position,
+ data,
+ handle_offset + len(handles))
+ to_pack.append(entry)
+ handles.extend(new_handles)
+ position = position + group.GetByteSize()
+ self._GetMainStruct().pack_into(data, HEADER_STRUCT.size, *to_pack)
+ return (data, handles)
+
+ def Deserialize(self, fields, data, handles):
+ if not isinstance(data, buffer):
+ data = buffer(data)
+ (_, version) = HEADER_STRUCT.unpack_from(data)
+ version_struct = self._GetStruct(version)
+ entitities = version_struct.unpack_from(data, HEADER_STRUCT.size)
+ filtered_groups = self._GetGroups(version)
+ position = HEADER_STRUCT.size
+ for (group, value) in zip(filtered_groups, entitities):
+ position = position + NeededPaddingForAlignment(position,
+ group.GetByteSize())
+ fields.update(group.Deserialize(value, buffer(data, position), handles))
+ position += group.GetByteSize()
+
+
+def NeededPaddingForAlignment(value, alignment=8):
+ """Returns the padding necessary to align value with the given alignment."""
+ if value % alignment:
+ return alignment - (value % alignment)
+ return 0
+
+
+def _GetVersion(groups):
+ return sum([len(x.descriptors) for x in groups])
+
+
+def _FilterGroups(groups, version):
+ return [group for group in groups if group.GetVersion() < version]
+
+
+def _GetStruct(groups):
+ index = 0
+ codes = [ '=' ]
+ for group in groups:
+ code = group.GetTypeCode()
+ size = group.GetByteSize()
+ needed_padding = NeededPaddingForAlignment(index, size)
+ if needed_padding:
+ codes.append('x' * needed_padding)
+ index = index + needed_padding
+ codes.append(code)
+ index = index + size
+ alignment_needed = NeededPaddingForAlignment(index)
+ if alignment_needed:
+ codes.append('x' * alignment_needed)
+ return struct.Struct(''.join(codes))
diff --git a/mojo/public/python/mojo/c_core.pxd b/mojo/public/python/mojo/c_core.pxd
new file mode 100644
index 0000000..80b8487
--- /dev/null
+++ b/mojo/public/python/mojo/c_core.pxd
@@ -0,0 +1,201 @@
+# 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.
+
+# distutils: language = c++
+
+from cpython.buffer cimport PyBUF_CONTIG
+from cpython.buffer cimport PyBUF_CONTIG_RO
+from cpython.buffer cimport Py_buffer
+from cpython.buffer cimport PyBuffer_FillInfo
+from cpython.buffer cimport PyBuffer_Release
+from cpython.buffer cimport PyObject_GetBuffer
+from cpython.mem cimport PyMem_Malloc, PyMem_Free
+from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t
+
+cdef extern from "third_party/cython/python_export.h":
+ pass
+
+cdef extern from "mojo/public/platform/native/system_thunks.h" nogil:
+ cdef struct MojoSystemThunks:
+ pass
+
+cdef extern size_t MojoSetSystemThunks(const MojoSystemThunks* system_thunks)
+
+cdef extern from "mojo/public/c/system/core.h" nogil:
+ # types.h
+ ctypedef int64_t MojoTimeTicks
+
+ ctypedef uint32_t MojoHandle
+ const MojoHandle MOJO_HANDLE_INVALID
+
+ ctypedef int32_t MojoResult
+ const MojoResult MOJO_RESULT_OK
+ const MojoResult MOJO_RESULT_CANCELLED
+ const MojoResult MOJO_RESULT_UNKNOWN
+ const MojoResult MOJO_RESULT_INVALID_ARGUMENT
+ const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED
+ const MojoResult MOJO_RESULT_NOT_FOUND
+ const MojoResult MOJO_RESULT_ALREADY_EXISTS
+ const MojoResult MOJO_RESULT_PERMISSION_DENIED
+ const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED
+ const MojoResult MOJO_RESULT_FAILED_PRECONDITION
+ const MojoResult MOJO_RESULT_ABORTED
+ const MojoResult MOJO_RESULT_OUT_OF_RANGE
+ const MojoResult MOJO_RESULT_UNIMPLEMENTED
+ const MojoResult MOJO_RESULT_INTERNAL
+ const MojoResult MOJO_RESULT_UNAVAILABLE
+ const MojoResult MOJO_RESULT_DATA_LOSS
+ const MojoResult MOJO_RESULT_BUSY
+ const MojoResult MOJO_RESULT_SHOULD_WAIT
+
+ ctypedef uint64_t MojoDeadline
+ const MojoDeadline MOJO_DEADLINE_INDEFINITE
+
+ ctypedef uint32_t MojoHandleSignals
+ const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE
+ const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE
+ const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE
+
+ # functions.h
+ MojoTimeTicks MojoGetTimeTicksNow()
+ MojoResult MojoClose(MojoHandle handle)
+ MojoResult MojoWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline)
+ MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline)
+
+ # message_pipe.h
+ ctypedef uint32_t MojoCreateMessagePipeOptionsFlags
+ const MojoCreateMessagePipeOptionsFlags MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+
+ ctypedef uint32_t MojoWriteMessageFlags
+ const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE
+
+ ctypedef uint32_t MojoReadMessageFlags
+ const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE
+ const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD
+
+ cdef struct MojoCreateMessagePipeOptions:
+ uint32_t struct_size
+ MojoCreateMessagePipeOptionsFlags flags
+
+ MojoResult MojoCreateMessagePipe(
+ const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1)
+
+ MojoResult MojoWriteMessage(
+ MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags)
+
+ MojoResult MojoReadMessage(
+ MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags)
+
+ # data_pipe.h
+ ctypedef uint32_t MojoCreateDataPipeOptionsFlags
+ const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
+ const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD
+
+ cdef struct MojoCreateDataPipeOptions:
+ uint32_t struct_size
+ MojoCreateDataPipeOptionsFlags flags
+ uint32_t element_num_bytes
+ uint32_t capacity_num_bytes
+
+ ctypedef uint32_t MojoWriteDataFlags
+
+ const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE
+ const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+
+ ctypedef uint32_t MojoReadDataFlags
+
+ const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE
+ const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE
+ const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD
+ const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY
+
+ MojoResult MojoCreateDataPipe(
+ const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle)
+
+ MojoResult MojoWriteData(
+ MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags)
+
+ MojoResult MojoBeginWriteData(
+ MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags)
+
+ MojoResult MojoEndWriteData(
+ MojoHandle data_pipe_producer_handle,
+ uint32_t num_bytes_written)
+
+ MojoResult MojoReadData(
+ MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags)
+
+ MojoResult MojoBeginReadData(
+ MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags)
+
+ MojoResult MojoEndReadData(
+ MojoHandle data_pipe_consumer_handle,
+ uint32_t num_bytes_read)
+
+ # buffer.h
+ ctypedef uint32_t MojoCreateSharedBufferOptionsFlags
+ const MojoCreateSharedBufferOptionsFlags MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE
+
+ cdef struct MojoCreateSharedBufferOptions:
+ uint32_t struct_size
+ MojoCreateSharedBufferOptionsFlags flags
+
+ ctypedef uint32_t MojoDuplicateBufferHandleOptionsFlags
+ const MojoDuplicateBufferHandleOptionsFlags MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+
+ cdef struct MojoDuplicateBufferHandleOptions:
+ uint32_t struct_size
+ MojoDuplicateBufferHandleOptionsFlags flags
+
+ ctypedef uint32_t MojoMapBufferFlags
+ const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE
+
+ MojoResult MojoCreateSharedBuffer(
+ const MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle)
+
+ MojoResult MojoDuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle)
+
+ MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags)
+
+ MojoResult MojoUnmapBuffer(void* buffer)
diff --git a/mojo/public/python/mojo/c_environment.pxd b/mojo/public/python/mojo/c_environment.pxd
new file mode 100644
index 0000000..13a0934
--- /dev/null
+++ b/mojo/public/python/mojo/c_environment.pxd
@@ -0,0 +1,48 @@
+# 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.
+
+# distutils: language = c++
+
+from libc.stdint cimport int64_t, intptr_t, uint32_t, uint64_t
+
+
+cdef extern from "mojo/public/c/system/core.h" nogil:
+ ctypedef uint32_t MojoHandle
+ ctypedef uint64_t MojoDeadline
+ ctypedef uint32_t MojoHandleSignals
+
+
+cdef extern from "mojo/public/cpp/bindings/callback.h" nogil:
+ cdef cppclass CClosure "mojo::Callback<void()>":
+ CClosure()
+
+
+cdef extern from "mojo/public/c/environment/async_waiter.h" nogil:
+ ctypedef intptr_t MojoAsyncWaitID
+
+
+cdef extern from "mojo/public/python/src/python_system_helper.h" \
+ namespace "mojo::python" nogil:
+ cdef CClosure BuildClosure(object)
+ cdef cppclass PythonAsyncWaiter "mojo::python::PythonAsyncWaiter":
+ PythonAsyncWaiter()
+ MojoAsyncWaitID AsyncWait(MojoHandle,
+ MojoHandleSignals,
+ MojoDeadline,
+ object)
+ void CancelWait(MojoAsyncWaitID)
+
+
+cdef extern from "mojo/public/cpp/utility/run_loop.h" nogil:
+ cdef cppclass CRunLoop "mojo::RunLoop":
+ CRunLoop()
+ void Run() except *
+ void RunUntilIdle() except *
+ void Quit()
+ void PostDelayedTask(CClosure&, int64_t)
+
+
+cdef extern from "mojo/public/cpp/environment/environment.h" nogil:
+ cdef cppclass CEnvironment "mojo::Environment":
+ CEnvironment()
diff --git a/mojo/public/python/mojo/system.pyx b/mojo/public/python/mojo/system.pyx
new file mode 100644
index 0000000..4768247
--- /dev/null
+++ b/mojo/public/python/mojo/system.pyx
@@ -0,0 +1,741 @@
+# 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.
+
+# distutils language = c++
+
+cimport c_core
+cimport c_environment
+
+
+from cpython.buffer cimport PyBUF_CONTIG
+from cpython.buffer cimport PyBUF_CONTIG_RO
+from cpython.buffer cimport Py_buffer
+from cpython.buffer cimport PyBuffer_FillInfo
+from cpython.buffer cimport PyBuffer_Release
+from cpython.buffer cimport PyObject_GetBuffer
+from cpython.mem cimport PyMem_Malloc, PyMem_Free
+from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t
+
+def SetSystemThunks(system_thunks_as_object):
+ """Bind the basic Mojo Core functions.
+
+ This should only be used by the embedder.
+ """
+ cdef const c_core.MojoSystemThunks* system_thunks = (
+ <const c_core.MojoSystemThunks*><uintptr_t>system_thunks_as_object)
+ c_core.MojoSetSystemThunks(system_thunks)
+
+HANDLE_INVALID = c_core.MOJO_HANDLE_INVALID
+RESULT_OK = c_core.MOJO_RESULT_OK
+RESULT_CANCELLED = c_core.MOJO_RESULT_CANCELLED
+RESULT_UNKNOWN = c_core.MOJO_RESULT_UNKNOWN
+RESULT_INVALID_ARGUMENT = c_core.MOJO_RESULT_INVALID_ARGUMENT
+RESULT_DEADLINE_EXCEEDED = c_core.MOJO_RESULT_DEADLINE_EXCEEDED
+RESULT_NOT_FOUND = c_core.MOJO_RESULT_NOT_FOUND
+RESULT_ALREADY_EXISTS = c_core.MOJO_RESULT_ALREADY_EXISTS
+RESULT_PERMISSION_DENIED = c_core.MOJO_RESULT_PERMISSION_DENIED
+RESULT_RESOURCE_EXHAUSTED = c_core.MOJO_RESULT_RESOURCE_EXHAUSTED
+RESULT_FAILED_PRECONDITION = c_core.MOJO_RESULT_FAILED_PRECONDITION
+RESULT_ABORTED = c_core.MOJO_RESULT_ABORTED
+RESULT_OUT_OF_RANGE = c_core.MOJO_RESULT_OUT_OF_RANGE
+RESULT_UNIMPLEMENTED = c_core.MOJO_RESULT_UNIMPLEMENTED
+RESULT_INTERNAL = c_core.MOJO_RESULT_INTERNAL
+RESULT_UNAVAILABLE = c_core.MOJO_RESULT_UNAVAILABLE
+RESULT_DATA_LOSS = c_core.MOJO_RESULT_DATA_LOSS
+RESULT_BUSY = c_core.MOJO_RESULT_BUSY
+RESULT_SHOULD_WAIT = c_core.MOJO_RESULT_SHOULD_WAIT
+DEADLINE_INDEFINITE = c_core.MOJO_DEADLINE_INDEFINITE
+HANDLE_SIGNAL_NONE = c_core.MOJO_HANDLE_SIGNAL_NONE
+HANDLE_SIGNAL_READABLE = c_core.MOJO_HANDLE_SIGNAL_READABLE
+HANDLE_SIGNAL_WRITABLE = c_core.MOJO_HANDLE_SIGNAL_WRITABLE
+WRITE_MESSAGE_FLAG_NONE = c_core.MOJO_WRITE_MESSAGE_FLAG_NONE
+READ_MESSAGE_FLAG_NONE = c_core.MOJO_READ_MESSAGE_FLAG_NONE
+READ_MESSAGE_FLAG_MAY_DISCARD = c_core.MOJO_READ_MESSAGE_FLAG_MAY_DISCARD
+WRITE_DATA_FLAG_NONE = c_core.MOJO_WRITE_DATA_FLAG_NONE
+WRITE_DATA_FLAG_ALL_OR_NONE = c_core.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+READ_DATA_FLAG_NONE = c_core.MOJO_READ_DATA_FLAG_NONE
+READ_DATA_FLAG_ALL_OR_NONE = c_core.MOJO_READ_DATA_FLAG_ALL_OR_NONE
+READ_DATA_FLAG_DISCARD = c_core.MOJO_READ_DATA_FLAG_DISCARD
+READ_DATA_FLAG_QUERY = c_core.MOJO_READ_DATA_FLAG_QUERY
+MAP_BUFFER_FLAG_NONE = c_core.MOJO_MAP_BUFFER_FLAG_NONE
+
+def GetTimeTicksNow():
+ """Monotonically increasing tick count representing "right now."
+
+ See mojo/public/c/system/functions.h
+ """
+ return c_core.MojoGetTimeTicksNow()
+
+cdef class _ScopedMemory:
+ """Allocate memory at creation, and deallocate it at destruction."""
+ cdef void* memory
+ def __init__(self, size):
+ self.memory = PyMem_Malloc(size)
+
+ def __dealloc__(self):
+ PyMem_Free(self.memory)
+
+cdef class _ScopedBuffer:
+ """Retrieve pointer to a buffer a creation, and release it at destruction.
+ """
+ cdef Py_buffer _buf
+ cdef void* buf
+ cdef Py_ssize_t len
+
+ def __init__(self, obj, flags=PyBUF_CONTIG_RO):
+ if obj:
+ if PyObject_GetBuffer(obj, &self._buf, flags) < 0:
+ raise TypeError('Unable to read buffer.')
+ self.buf = self._buf.buf
+ self.len = self._buf.len
+ else:
+ self.buf = NULL
+ self.len = 0
+
+ def __dealloc__(self):
+ if self.buf:
+ PyBuffer_Release(&self._buf)
+
+def _SliceBuffer(buffer, size):
+ """Slice the given buffer, reducing it to the given size.
+
+ Return None if None is passed in.
+ """
+ if not buffer:
+ return buffer
+ return buffer[:size]
+
+cdef class _NativeMemoryView(object):
+ """Create a python buffer wrapping the given memory.
+
+ Will also retain the given handle until this object is deallocated.
+ """
+ cdef void* _memory
+ cdef uint32_t _size
+ cdef char _read_only
+ cdef char _wrapped
+ cdef object _handle
+
+ def __init__(self, handle):
+ self._handle = handle
+
+ def __cinit__(self):
+ self._memory = NULL
+ self._size = 0
+ self._read_only = True
+ self._wrapped = False
+
+ cdef Wrap(self,
+ const void* memory,
+ uint32_t size,
+ read_only=True):
+ """Makes this buffer wraps the given memory.
+
+ Must be called before using this buffer, and must only be called once.
+ """
+ assert not self._wrapped
+ self._wrapped = True
+ self._memory = <void*>memory
+ self._size = size
+ self._read_only = read_only
+
+ # buffer interface (PEP 3118)
+ def __getbuffer__(self, Py_buffer *view, int flags):
+ assert self._wrapped
+ if view == NULL:
+ return
+ PyBuffer_FillInfo(view,
+ self,
+ self._memory,
+ self._size,
+ self._read_only,
+ flags)
+
+ def __releasebuffer__(self, Py_buffer *view):
+ assert self._wrapped
+ pass
+
+ # legacy buffer interface
+ def __getsegcount__(self, Py_ssize_t *sizes):
+ assert self._wrapped
+ if sizes != NULL:
+ sizes[0] = self._size
+ return 1
+
+ def __getreadbuffer__(self, Py_ssize_t index, void **data):
+ assert self._wrapped
+ if index != 0:
+ raise SystemError('Index out of bounds: %d' % index)
+ data[0] = self._memory
+ return self._size
+
+ def __getwritebuffer__(self, Py_ssize_t index, void **data):
+ assert self._wrapped
+ if index != 0:
+ raise SystemError('Index out of bounds: %d' % index)
+ if self._read_only:
+ raise TypeError('Buffer is read-only.')
+ data[0] = self._memory
+ return self._size
+
+class MojoException(Exception):
+ """Exception wrapping a mojo result error code."""
+
+ def __init__(self, mojo_result):
+ self.mojo_result = mojo_result
+
+def WaitMany(handles_and_signals, deadline):
+ """Waits on a list of handles.
+
+ Args:
+ handles_and_signals: list of tuples of handle and signal.
+
+ See mojo/public/c/system/functions.h
+ """
+ cdef uint32_t length = len(handles_and_signals)
+ cdef _ScopedMemory handles_alloc = _ScopedMemory(
+ sizeof(c_core.MojoHandle) * length)
+ cdef _ScopedMemory signals_alloc = _ScopedMemory(
+ sizeof(c_core.MojoHandleSignals) * length)
+ cdef c_core.MojoHandle* handles = <c_core.MojoHandle*>handles_alloc.memory
+ cdef c_core.MojoHandleSignals* signals = (
+ <c_core.MojoHandleSignals*>signals_alloc.memory)
+ cdef int index = 0
+ for (h, s) in handles_and_signals:
+ handles[index] = (<Handle?>h)._mojo_handle
+ signals[index] = s
+ index += 1
+ cdef c_core.MojoResult result = c_core.MOJO_RESULT_OK
+ cdef c_core.MojoDeadline cdeadline = deadline
+ with nogil:
+ result = c_core.MojoWaitMany(handles, signals, length, cdeadline)
+ return result
+
+cdef class DataPipeTwoPhaseBuffer(object):
+ """Return value for two phases read and write.
+
+ The buffer field contains the python buffer where data can be read or written.
+ When done with the buffer, the |end| method must be called with the number of
+ bytes read or written.
+ """
+
+ cdef object _buffer
+ cdef Handle _handle
+ cdef char _read
+
+ def __init__(self, handle, buffer, read=True):
+ self._buffer = buffer
+ self._handle = handle
+ self._read = read
+
+ def End(self, num_bytes):
+ self._buffer = None
+ cdef c_core.MojoResult result
+ if self._read:
+ result = c_core.MojoEndReadData(self._handle._mojo_handle, num_bytes)
+ else:
+ result = c_core.MojoEndWriteData(self._handle._mojo_handle, num_bytes)
+ self._handle = None
+ return result
+
+ @property
+ def buffer(self):
+ return self._buffer
+
+ def __dealloc__(self):
+ assert not self._buffer
+
+cdef class MappedBuffer(object):
+ """Return value for the |map| operation on shared buffer handles.
+
+ The buffer field contains the python buffer where data can be read or written.
+ When done with the buffer, the |unmap| method must be called.
+ """
+
+ cdef object _buffer
+ cdef object _handle
+ cdef object _cleanup
+
+ def __init__(self, handle, buffer, cleanup):
+ self._buffer = buffer
+ self._handle = handle
+ self._cleanup = cleanup
+
+ def UnMap(self):
+ self._buffer = None
+ cdef c_core.MojoResult result = self._cleanup()
+ self._cleanup = None
+ self._handle = None
+ return result
+
+ @property
+ def buffer(self):
+ return self._buffer
+
+ def __dealloc__(self):
+ if self._buffer:
+ self.UnMap()
+
+cdef class Handle(object):
+ """A mojo object."""
+
+ cdef c_core.MojoHandle _mojo_handle
+
+ def __init__(self, mojo_handle=c_core.MOJO_HANDLE_INVALID):
+ self._mojo_handle = mojo_handle
+
+ def _Invalidate(self):
+ """Invalidate the current handle.
+
+ The close operation is not called. It is the responsability of the caller to
+ ensure that the handle is not leaked.
+ """
+ self._mojo_handle = c_core.MOJO_HANDLE_INVALID
+
+ def IsValid(self):
+ """Returns whether this handle is valid."""
+ return self._mojo_handle != c_core.MOJO_HANDLE_INVALID
+
+ def Close(self):
+ """Closes this handle.
+
+ See mojo/public/c/system/functions.h
+ """
+ cdef c_core.MojoResult result = c_core.MOJO_RESULT_OK
+ if self.IsValid():
+ result = c_core.MojoClose(self._mojo_handle)
+ self._Invalidate()
+ return result
+
+ def __dealloc__(self):
+ self.Close()
+
+ def Wait(self, signals, deadline):
+ """Waits on the given handle.
+
+ See mojo/public/c/system/functions.h
+ """
+ cdef c_core.MojoHandle handle = self._mojo_handle
+ cdef c_core.MojoHandleSignals csignals = signals
+ cdef c_core.MojoDeadline cdeadline = deadline
+ cdef c_core.MojoResult result
+ with nogil:
+ result = c_core.MojoWait(handle, csignals, cdeadline)
+ return result
+
+ def AsyncWait(self, signals, deadline, callback):
+ cdef c_core.MojoHandle handle = self._mojo_handle
+ cdef c_core.MojoHandleSignals csignals = signals
+ cdef c_core.MojoDeadline cdeadline = deadline
+ cdef c_environment.MojoAsyncWaitID wait_id = _ASYNC_WAITER.AsyncWait(
+ handle,
+ csignals,
+ cdeadline,
+ callback)
+ def cancel():
+ _ASYNC_WAITER.CancelWait(wait_id)
+ return cancel
+
+ def WriteMessage(self,
+ buffer=None,
+ handles=None,
+ flags=WRITE_MESSAGE_FLAG_NONE):
+ """Writes a message to the message pipe.
+
+ This method can only be used on a handle obtained from |MessagePipe()|.
+
+ See mojo/public/c/system/message_pipe.h
+ """
+ cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer)
+ cdef uint32_t input_buffer_length = buffer_as_buffer.len
+ cdef c_core.MojoHandle* input_handles = NULL
+ cdef uint32_t input_handles_length = 0
+ cdef _ScopedMemory handles_alloc = None
+ if handles:
+ input_handles_length = len(handles)
+ handles_alloc = _ScopedMemory(sizeof(c_core.MojoHandle) *
+ input_handles_length)
+ input_handles = <c_core.MojoHandle*>handles_alloc.memory
+ for i in xrange(input_handles_length):
+ input_handles[i] = (<Handle?>handles[i])._mojo_handle
+ cdef c_core.MojoResult res = c_core.MojoWriteMessage(self._mojo_handle,
+ buffer_as_buffer.buf,
+ input_buffer_length,
+ input_handles,
+ input_handles_length,
+ flags)
+ if res == c_core.MOJO_RESULT_OK and handles:
+ # Handles have been transferred. Let's invalidate those.
+ for handle in handles:
+ handle._Invalidate()
+ return res
+
+ def ReadMessage(self,
+ buffer=None,
+ max_number_of_handles=0,
+ flags=READ_MESSAGE_FLAG_NONE):
+ """Reads a message from the message pipe.
+
+ This method can only be used on a handle obtained from |MessagePipe()|.
+
+ This method returns a triplet of value (code, data, sizes):
+ - if code is RESULT_OK, sizes will be None, and data will be a pair of
+ (buffer, handles) where buffer is a view of the input buffer with the read
+ data, and handles is a list of received handles.
+ - if code is RESULT_RESOURCE_EXHAUSTED, data will be None and sizes will be
+ a pair of (buffer_size, handles_size) where buffer_size is the size of the
+ next message data and handles_size is the number of handles in the next
+ message.
+ - if code is any other value, data and sizes will be None.
+
+ See mojo/public/c/system/message_pipe.h
+ """
+ cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer, PyBUF_CONTIG)
+ cdef uint32_t input_buffer_length = buffer_as_buffer.len
+ cdef c_core.MojoHandle* input_handles = NULL
+ cdef uint32_t input_handles_length = 0
+ cdef _ScopedMemory handles_alloc = None
+ if max_number_of_handles > 0:
+ input_handles_length = max_number_of_handles
+ handles_alloc = _ScopedMemory(sizeof(c_core.MojoHandle) *
+ input_handles_length)
+ input_handles = <c_core.MojoHandle*>handles_alloc.memory
+ cdef res = c_core.MojoReadMessage(self._mojo_handle,
+ buffer_as_buffer.buf,
+ &input_buffer_length,
+ input_handles,
+ &input_handles_length,
+ flags)
+ if res == c_core.MOJO_RESULT_RESOURCE_EXHAUSTED:
+ return (res, None, (input_buffer_length, input_handles_length))
+ if res == c_core.MOJO_RESULT_OK:
+ returned_handles = [Handle(input_handles[i])
+ for i in xrange(input_handles_length)]
+ return (res,
+ (_SliceBuffer(buffer, input_buffer_length), returned_handles),
+ None)
+ return (res, None, None)
+
+ def WriteData(self, buffer=None, flags=WRITE_DATA_FLAG_NONE):
+ """
+ Writes the given data to the data pipe producer.
+
+ This method can only be used on a producer handle obtained from
+ |DataPipe()|.
+
+ This method returns a tuple (code, num_bytes).
+ - If code is RESULT_OK, num_bytes is the number of written bytes.
+ - Otherwise, num_bytes is None.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer)
+ cdef uint32_t input_buffer_length = buffer_as_buffer.len
+ cdef c_core.MojoResult res = c_core.MojoWriteData(self._mojo_handle,
+ buffer_as_buffer.buf,
+ &input_buffer_length,
+ flags)
+ if res == c_core.MOJO_RESULT_OK:
+ return (res, input_buffer_length)
+ return (res, None)
+
+ def BeginWriteData(self,
+ min_size=None,
+ flags=WRITE_DATA_FLAG_NONE):
+ """
+ Begins a two-phase write to the data pipe producer.
+
+ This method can only be used on a producer handle obtained from
+ |DataPipe()|.
+
+ This method returns a tuple (code, two_phase_buffer).
+ - If code is RESULT_OK, two_phase_buffer is a writable
+ DataPipeTwoPhaseBuffer
+ - Otherwise, two_phase_buffer is None.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ cdef void* out_buffer
+ cdef uint32_t out_size = 0
+ if min_size:
+ flags |= c_core.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
+ out_size = min_size
+ cdef c_core.MojoResult res = c_core.MojoBeginWriteData(self._mojo_handle,
+ &out_buffer,
+ &out_size,
+ flags)
+ if res != c_core.MOJO_RESULT_OK:
+ return (res, None)
+ cdef _NativeMemoryView view_buffer = _NativeMemoryView(self)
+ view_buffer.Wrap(out_buffer, out_size, read_only=False)
+ return (res, DataPipeTwoPhaseBuffer(self, memoryview(view_buffer), False))
+
+ def ReadData(self, buffer=None, flags=READ_DATA_FLAG_NONE):
+ """Reads data from the data pipe consumer.
+
+ This method can only be used on a consumer handle obtained from
+ |DataPipe()|.
+
+ This method returns a tuple (code, buffer)
+ - if code is RESULT_OK, buffer will be a view of the input buffer with the
+ read data.
+ - otherwise, buffer will be None.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer)
+ cdef uint32_t input_buffer_length = buffer_as_buffer.len
+ cdef c_core.MojoResult res = c_core.MojoReadData(self._mojo_handle,
+ buffer_as_buffer.buf,
+ &input_buffer_length,
+ flags)
+ if res == c_core.MOJO_RESULT_OK:
+ return (res, _SliceBuffer(buffer, input_buffer_length))
+ return (res, None)
+
+ def QueryData(self, flags=READ_DATA_FLAG_NONE):
+ """Queries the amount of data available on the data pipe consumer.
+
+ This method can only be used on a consumer handle obtained from
+ |DataPipe()|.
+
+ This method returns a tuple (code, num_bytes)
+ - if code is RESULT_OK, num_bytes will be the number of bytes available on
+ the data pipe consumer.
+ - otherwise, num_bytes will be None.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ cdef uint32_t num_bytes = 0
+ cdef c_core.MojoResult res = c_core.MojoReadData(
+ self._mojo_handle,
+ NULL,
+ &num_bytes,
+ flags|c_core.MOJO_READ_DATA_FLAG_QUERY)
+ return (res, num_bytes)
+
+ def BeginReadData(self, min_size=None, flags=READ_DATA_FLAG_NONE):
+ """
+ Begins a two-phase read to the data pipe consumer.
+
+ This method can only be used on a consumer handle obtained from
+ |DataPipe()|.
+
+ This method returns a tuple (code, two_phase_buffer).
+ - If code is RESULT_OK, two_phase_buffer is a readable
+ DataPipeTwoPhaseBuffer
+ - Otherwise, two_phase_buffer is None.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ cdef const void* out_buffer
+ cdef uint32_t out_size = 0
+ if min_size:
+ flags |= c_core.MOJO_READ_DATA_FLAG_ALL_OR_NONE
+ out_size = min_size
+ cdef c_core.MojoResult res = c_core.MojoBeginReadData(self._mojo_handle,
+ &out_buffer,
+ &out_size,
+ flags)
+ if res != c_core.MOJO_RESULT_OK:
+ return (res, None)
+ cdef _NativeMemoryView view_buffer = _NativeMemoryView(self)
+ view_buffer.Wrap(out_buffer, out_size, read_only=True)
+ return (res, DataPipeTwoPhaseBuffer(self, memoryview(view_buffer), True))
+
+ def Duplicate(self, options=None):
+ """Duplicate the shared buffer handle.
+
+ This method can only be used on a handle obtained from
+ |CreateSharedBuffer()| or |Duplicate()|.
+
+ See mojo/public/c/system/buffer.h
+ """
+ cdef c_core.MojoDuplicateBufferHandleOptions coptions
+ cdef c_core.MojoDuplicateBufferHandleOptions* coptions_ptr = NULL
+ cdef c_core.MojoHandle cnew_handle = c_core.MOJO_HANDLE_INVALID
+ if options:
+ coptions.struct_size = sizeof(c_core.MojoDuplicateBufferHandleOptions)
+ coptions.flags = options.flags
+ coptions_ptr = &coptions
+ cdef c_core.MojoResult result = c_core.MojoDuplicateBufferHandle(
+ self._mojo_handle, coptions_ptr, &cnew_handle)
+ new_handle = Handle(cnew_handle)
+ if result != c_core.MOJO_RESULT_OK:
+ raise MojoException(result)
+ return new_handle
+
+ def Map(self, offset, num_bytes, flags=MAP_BUFFER_FLAG_NONE):
+ """Maps the part (at offset |offset| of length |num_bytes|) of the buffer.
+
+ This method can only be used on a handle obtained from
+ |CreateSharedBuffer()| or |Duplicate()|.
+
+ This method returns a tuple (code, mapped_buffer).
+ - If code is RESULT_OK, mapped_buffer is a readable/writable
+ MappedBuffer
+ - Otherwise, mapped_buffer is None.
+
+ See mojo/public/c/system/buffer.h
+ """
+ cdef void* buffer
+ res = c_core.MojoMapBuffer(self._mojo_handle,
+ offset,
+ num_bytes,
+ &buffer,
+ flags)
+ if res != c_core.MOJO_RESULT_OK:
+ return (res, None)
+ cdef _NativeMemoryView view_buffer = _NativeMemoryView(self)
+ view_buffer.Wrap(buffer, num_bytes, read_only=False)
+ return (res, MappedBuffer(self,
+ memoryview(view_buffer),
+ lambda: c_core.MojoUnmapBuffer(buffer)))
+
+class CreateMessagePipeOptions(object):
+ """Options for creating a message pipe.
+
+ See mojo/public/c/system/message_pipe.h
+ """
+ FLAG_NONE = c_core.MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+
+ def __init__(self):
+ self.flags = CreateMessagePipeOptions.FLAG_NONE
+
+class MessagePipe(object):
+ """Creates a message pipe.
+
+ The two ends of the message pipe are accessible with the members handle0 and
+ handle1.
+
+ See mojo/public/c/system/message_pipe.h
+ """
+ def __init__(self, options=None):
+ cdef c_core.MojoCreateMessagePipeOptions coptions
+ cdef c_core.MojoCreateMessagePipeOptions* coptions_ptr = NULL
+ cdef c_core.MojoHandle chandle0 = c_core.MOJO_HANDLE_INVALID
+ cdef c_core.MojoHandle chandle1 = c_core.MOJO_HANDLE_INVALID
+ if options:
+ coptions.struct_size = sizeof(c_core.MojoCreateMessagePipeOptions)
+ coptions.flags = options.flags
+ coptions_ptr = &coptions
+ cdef c_core.MojoResult result = c_core.MojoCreateMessagePipe(coptions_ptr,
+ &chandle0,
+ &chandle1)
+ self.handle0 = Handle(chandle0)
+ self.handle1 = Handle(chandle1)
+ if result != c_core.MOJO_RESULT_OK:
+ raise c_core.MojoException(result)
+
+
+class CreateDataPipeOptions(object):
+ """Options for creating a data pipe.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ FLAG_NONE = c_core.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
+ FLAG_MAY_DISCARD = c_core.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD
+
+ def __init__(self):
+ self.flags = CreateDataPipeOptions.FLAG_NONE
+ self.element_num_bytes = 1
+ self.capacity_num_bytes = 0
+
+class DataPipe(object):
+ """Creates a data pipe.
+
+ The producer end of the data pipe is accessible with the member
+ producer_handle and the consumer end of the data pipe is accessible with the
+ member cconsumer_handle.
+
+ See mojo/public/c/system/data_pipe.h
+ """
+ def __init__(self, options=None):
+ cdef c_core.MojoCreateDataPipeOptions coptions
+ cdef c_core.MojoCreateDataPipeOptions* coptions_ptr = NULL
+ cdef c_core.MojoHandle cproducer_handle = c_core.MOJO_HANDLE_INVALID
+ cdef c_core.MojoHandle cconsumer_handle = c_core.MOJO_HANDLE_INVALID
+ if options:
+ coptions.struct_size = sizeof(c_core.MojoCreateDataPipeOptions)
+ coptions.flags = options.flags
+ coptions.element_num_bytes = options.element_num_bytes
+ coptions.capacity_num_bytes = options.capacity_num_bytes
+ coptions_ptr = &coptions
+ cdef c_core.MojoResult result = c_core.MojoCreateDataPipe(coptions_ptr,
+ &cproducer_handle,
+ &cconsumer_handle)
+ self.producer_handle = Handle(cproducer_handle)
+ self.consumer_handle = Handle(cconsumer_handle)
+ if result != c_core.MOJO_RESULT_OK:
+ raise MojoException(result)
+
+class CreateSharedBufferOptions(object):
+ """Options for creating a shared buffer.
+
+ See mojo/public/c/system/buffer.h
+ """
+ FLAG_NONE = c_core.MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE
+
+ def __init__(self):
+ self.flags = CreateSharedBufferOptions.FLAG_NONE
+
+def CreateSharedBuffer(num_bytes, options=None):
+ """Creates a buffer of size |num_bytes| bytes that can be shared.
+
+ See mojo/public/c/system/buffer.h
+ """
+ cdef c_core.MojoCreateSharedBufferOptions coptions
+ cdef c_core.MojoCreateSharedBufferOptions* coptions_ptr = NULL
+ cdef c_core.MojoHandle chandle = c_core.MOJO_HANDLE_INVALID
+ if options:
+ coptions.struct_size = sizeof(c_core.MojoCreateSharedBufferOptions)
+ coptions.flags = options.flags
+ coptions_ptr = &coptions
+ cdef c_core.MojoResult result = c_core.MojoCreateSharedBuffer(coptions_ptr,
+ num_bytes,
+ &chandle)
+ handle = Handle(chandle)
+ if result != c_core.MOJO_RESULT_OK:
+ raise MojoException(result)
+ return handle
+
+class DuplicateSharedBufferOptions(object):
+ """Options for duplicating a shared buffer.
+
+ See mojo/public/c/system/buffer.h
+ """
+ FLAG_NONE = c_core.MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+
+ def __init__(self):
+ self.flags = DuplicateSharedBufferOptions.FLAG_NONE
+
+
+cdef class RunLoop(object):
+ """RunLoop to use when using asynchronous operations on handles."""
+
+ cdef c_environment.CRunLoop c_run_loop
+
+ def Run(self):
+ """Run the runloop until Quit is called."""
+ self.c_run_loop.Run()
+
+ def RunUntilIdle(self):
+ """Run the runloop until Quit is called or no operation is waiting."""
+ self.c_run_loop.RunUntilIdle()
+
+ def Quit(self):
+ """Quit the runloop."""
+ self.c_run_loop.Quit()
+
+ def PostDelayedTask(self, runnable, delay=0):
+ """
+ Post a task on the runloop. This must be called from the thread owning the
+ runloop.
+ """
+ cdef c_environment.CClosure closure = c_environment.BuildClosure(runnable)
+ self.c_run_loop.PostDelayedTask(closure, delay)
+
+
+cdef c_environment.CEnvironment* _ENVIRONMENT = new c_environment.CEnvironment()
+cdef c_environment.PythonAsyncWaiter* _ASYNC_WAITER = new c_environment.PythonAsyncWaiter()
diff --git a/mojo/public/python/src/python_system_helper.cc b/mojo/public/python/src/python_system_helper.cc
new file mode 100644
index 0000000..f42e671
--- /dev/null
+++ b/mojo/public/python/src/python_system_helper.cc
@@ -0,0 +1,171 @@
+// 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/python/src/python_system_helper.h"
+
+#include "Python.h"
+
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace {
+
+class ScopedGIL {
+ public:
+ ScopedGIL() { state_ = PyGILState_Ensure(); }
+
+ ~ScopedGIL() { PyGILState_Release(state_); }
+
+ private:
+ PyGILState_STATE state_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedGIL);
+};
+
+class PythonClosure : public mojo::Closure::Runnable {
+ public:
+ PythonClosure(PyObject* callable) : callable_(callable) {
+ MOJO_DCHECK(callable);
+ Py_XINCREF(callable);
+ }
+
+ virtual ~PythonClosure() {
+ ScopedGIL acquire_gil;
+ Py_DECREF(callable_);
+ }
+
+ virtual void Run() const override {
+ ScopedGIL acquire_gil;
+ PyObject* empty_tuple = PyTuple_New(0);
+ if (!empty_tuple) {
+ mojo::RunLoop::current()->Quit();
+ return;
+ }
+
+ PyObject* result = PyObject_CallObject(callable_, empty_tuple);
+ Py_DECREF(empty_tuple);
+ if (result) {
+ Py_DECREF(result);
+ } else {
+ mojo::RunLoop::current()->Quit();
+ return;
+ }
+ }
+
+ private:
+ PyObject* callable_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(PythonClosure);
+};
+
+void AsyncCallbackForwarder(void* closure, MojoResult result) {
+ mojo::Callback<void(MojoResult)>* callback =
+ static_cast<mojo::Callback<void(MojoResult)>*>(closure);
+ // callback will be deleted when it is run.
+ callback->Run(result);
+}
+
+} // namespace
+
+namespace mojo {
+namespace python {
+
+class PythonAsyncWaiter::AsyncWaiterRunnable
+ : public mojo::Callback<void(MojoResult)>::Runnable {
+ public:
+ AsyncWaiterRunnable(PyObject* callable, CallbackMap* callbacks)
+ : wait_id_(0), callable_(callable), callbacks_(callbacks) {
+ MOJO_DCHECK(callable);
+ MOJO_DCHECK(callbacks_);
+ Py_XINCREF(callable);
+ }
+
+ virtual ~AsyncWaiterRunnable() {
+ ScopedGIL acquire_gil;
+ Py_DECREF(callable_);
+ }
+
+ void set_wait_id(int wait_id) { wait_id_ = wait_id; }
+
+ virtual void Run(MojoResult mojo_result) const override {
+ MOJO_DCHECK(wait_id_);
+
+ // Remove to reference to this object from PythonAsyncWaiter and ensure this
+ // object will be destroyed when this method exits.
+ MOJO_DCHECK(callbacks_->find(wait_id_) != callbacks_->end());
+ internal::SharedPtr<mojo::Callback<void(MojoResult)> > self =
+ (*callbacks_)[wait_id_];
+ callbacks_->erase(wait_id_);
+
+ ScopedGIL acquire_gil;
+ PyObject* args_tuple = Py_BuildValue("(i)", mojo_result);
+ if (!args_tuple) {
+ mojo::RunLoop::current()->Quit();
+ return;
+ }
+
+ PyObject* result = PyObject_CallObject(callable_, args_tuple);
+ Py_DECREF(args_tuple);
+ if (result) {
+ Py_DECREF(result);
+ } else {
+ mojo::RunLoop::current()->Quit();
+ return;
+ }
+ }
+
+ private:
+ MojoAsyncWaitID wait_id_;
+ PyObject* callable_;
+ CallbackMap* callbacks_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterRunnable);
+};
+
+Closure BuildClosure(PyObject* callable) {
+ if (!PyCallable_Check(callable))
+ return Closure();
+
+ return Closure(
+ static_cast<mojo::Closure::Runnable*>(new PythonClosure(callable)));
+}
+
+PythonAsyncWaiter::PythonAsyncWaiter() {
+ async_waiter_ = Environment::GetDefaultAsyncWaiter();
+}
+
+PythonAsyncWaiter::~PythonAsyncWaiter() {
+ for (CallbackMap::const_iterator it = callbacks_.begin();
+ it != callbacks_.end();
+ ++it) {
+ async_waiter_->CancelWait(it->first);
+ }
+}
+
+MojoAsyncWaitID PythonAsyncWaiter::AsyncWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ PyObject* callable) {
+ AsyncWaiterRunnable* runner = new AsyncWaiterRunnable(callable, &callbacks_);
+ internal::SharedPtr<mojo::Callback<void(MojoResult)> > callback(
+ new mojo::Callback<void(MojoResult)>(
+ static_cast<mojo::Callback<void(MojoResult)>::Runnable*>(runner)));
+ MojoAsyncWaitID wait_id = async_waiter_->AsyncWait(
+ handle, signals, deadline, &AsyncCallbackForwarder, callback.get());
+ callbacks_[wait_id] = callback;
+ runner->set_wait_id(wait_id);
+ return wait_id;
+}
+
+void PythonAsyncWaiter::CancelWait(MojoAsyncWaitID wait_id) {
+ if (callbacks_.find(wait_id) != callbacks_.end()) {
+ async_waiter_->CancelWait(wait_id);
+ callbacks_.erase(wait_id);
+ }
+}
+
+} // namespace python
+} // namespace mojo
diff --git a/mojo/public/python/src/python_system_helper.h b/mojo/public/python/src/python_system_helper.h
new file mode 100644
index 0000000..e0d83ca
--- /dev/null
+++ b/mojo/public/python/src/python_system_helper.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_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_
+#define MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_
+
+// Python must be the first include, as it defines preprocessor variable without
+// checking if they already exist.
+#include <Python.h>
+
+#include <map>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+
+namespace mojo {
+namespace python {
+
+// Create a mojo::Closure from a callable python object.
+mojo::Closure BuildClosure(PyObject* callable);
+
+class PythonAsyncWaiter {
+ public:
+ PythonAsyncWaiter();
+ ~PythonAsyncWaiter();
+ MojoAsyncWaitID AsyncWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ PyObject* callable);
+
+ void CancelWait(MojoAsyncWaitID wait_id);
+
+ private:
+ class AsyncWaiterRunnable;
+
+ typedef std::map<MojoAsyncWaitID,
+ internal::SharedPtr<mojo::Callback<void(MojoResult)> > >
+ CallbackMap;
+
+ CallbackMap callbacks_;
+ const MojoAsyncWaiter* async_waiter_;
+};
+
+} // namespace python
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_
diff --git a/mojo/public/tests/DEPS b/mojo/public/tests/DEPS
new file mode 100644
index 0000000..0669117
--- /dev/null
+++ b/mojo/public/tests/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo",
+]
diff --git a/mojo/public/tests/test_support_private.cc b/mojo/public/tests/test_support_private.cc
new file mode 100644
index 0000000..fa5ead4
--- /dev/null
+++ b/mojo/public/tests/test_support_private.cc
@@ -0,0 +1,69 @@
+// 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/tests/test_support_private.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static mojo::test::TestSupport* g_test_support = NULL;
+
+extern "C" {
+
+void MojoTestSupportLogPerfResult(const char* test_name,
+ double value,
+ const char* units) {
+ if (g_test_support)
+ g_test_support->LogPerfResult(test_name, value, units);
+ else
+ printf("[no test runner]\t%s\t%g\t%s\n", test_name, value, units);
+}
+
+FILE* MojoTestSupportOpenSourceRootRelativeFile(const char* relative_path) {
+ if (g_test_support)
+ return g_test_support->OpenSourceRootRelativeFile(relative_path);
+ printf("[no test runner]\n");
+ return NULL;
+}
+
+char** MojoTestSupportEnumerateSourceRootRelativeDirectory(
+ const char* relative_path) {
+ if (g_test_support)
+ return g_test_support->EnumerateSourceRootRelativeDirectory(relative_path);
+
+ printf("[no test runner]\n");
+
+ // Return empty list:
+ char** rv = static_cast<char**>(calloc(1, sizeof(char*)));
+ rv[0] = NULL;
+ return rv;
+}
+
+} // extern "C"
+
+namespace mojo {
+namespace test {
+
+TestSupport::~TestSupport() {
+}
+
+// static
+void TestSupport::Init(TestSupport* test_support) {
+ assert(!g_test_support);
+ g_test_support = test_support;
+}
+
+// static
+TestSupport* TestSupport::Get() {
+ return g_test_support;
+}
+
+// static
+void TestSupport::Reset() {
+ g_test_support = NULL;
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/tests/test_support_private.h b/mojo/public/tests/test_support_private.h
new file mode 100644
index 0000000..29983ca
--- /dev/null
+++ b/mojo/public/tests/test_support_private.h
@@ -0,0 +1,36 @@
+// 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_TESTS_TEST_SUPPORT_PRIVATE_H_
+#define MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_
+
+#include <stdio.h>
+
+#include "mojo/public/c/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+// Implementors of the test support APIs can use this interface to install their
+// implementation into the mojo_test_support dynamic library.
+class MOJO_TEST_SUPPORT_EXPORT TestSupport {
+ public:
+ virtual ~TestSupport();
+
+ static void Init(TestSupport* test_support);
+ static TestSupport* Get();
+ static void Reset();
+
+ virtual void LogPerfResult(const char* test_name,
+ double value,
+ const char* units) = 0;
+ virtual FILE* OpenSourceRootRelativeFile(const char* relative_path) = 0;
+ virtual char** EnumerateSourceRootRelativeDirectory(
+ const char* relative_path) = 0;
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_TESTS_TEST_SUPPORT_PRIVATE_H_
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl
new file mode 100644
index 0000000..86c85d7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl
@@ -0,0 +1,9 @@
+enum {{enum.name}} {
+{%- for field in enum.fields %}
+{%- if field.value %}
+ {{enum.name|to_all_caps}}_{{field.name}} = {{field.value|expression_to_text}},
+{%- else %}
+ {{enum.name|to_all_caps}}_{{field.name}},
+{%- endif %}
+{%- endfor %}
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
new file mode 100644
index 0000000..30d20d8
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -0,0 +1,49 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy;
+class {{interface.name}}Stub;
+
+class {{interface.name}}RequestValidator;
+{%- if interface|has_callbacks %}
+class {{interface.name}}ResponseValidator;
+{%- endif %}
+{% if interface.client %}
+class {{interface.client}};
+{% endif %}
+
+class {{interface.name}} {
+ public:
+ static const char* Name_;
+
+ typedef {{interface.name}}Proxy Proxy_;
+ typedef {{interface.name}}Stub Stub_;
+
+ typedef {{interface.name}}RequestValidator RequestValidator_;
+{%- if interface|has_callbacks %}
+ typedef {{interface.name}}ResponseValidator ResponseValidator_;
+{%- else %}
+ typedef mojo::PassThroughFilter ResponseValidator_;
+{%- endif %}
+{% if interface.client %}
+ typedef {{interface.client}} Client;
+{% else %}
+ typedef mojo::NoInterface Client;
+{% endif %}
+
+{#--- Constants #}
+{%- for constant in interface.constants %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+
+{#--- Enums #}
+{%- for enum in interface.enums %}
+{% macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
+ {{enum_def()|indent(2)}}
+{%- endfor %}
+
+{#--- Methods #}
+ virtual ~{{interface.name}}() {}
+
+{%- for method in interface.methods %}
+ virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0;
+{%- endfor %}
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
new file mode 100644
index 0000000..bb5b777
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -0,0 +1,334 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+{%- set class_name = interface.name %}
+{%- set proxy_name = interface.name ~ "Proxy" %}
+{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
+
+{%- macro alloc_params(parameters) %}
+{%- for param in parameters %}
+{%- if param.kind|is_object_kind %}
+{{param.kind|cpp_result_type}} p{{loop.index}};
+Deserialize_(params->{{param.name}}.ptr, &p{{loop.index}});
+{% endif -%}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro pass_params(parameters) %}
+{%- for param in parameters %}
+{%- if param.kind|is_string_kind -%}
+p{{loop.index}}
+{%- elif param.kind|is_object_kind -%}
+p{{loop.index}}.Pass()
+{%- elif param.kind|is_interface_kind -%}
+mojo::MakeProxy<{{param.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}})))
+{%- elif param.kind|is_interface_request_kind -%}
+mojo::MakeRequest<{{param.kind.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}})))
+{%- elif param.kind|is_any_handle_kind -%}
+mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}}))
+{%- elif param.kind|is_enum_kind -%}
+static_cast<{{param.kind|cpp_wrapper_type}}>(params->{{param.name}})
+{%- else -%}
+params->{{param.name}}
+{%- endif -%}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro compute_payload_size(params_name, parameters) -%}
+ size_t payload_size =
+ mojo::internal::Align(sizeof({{params_name}}));
+{#--- Computes #}
+{%- for param in parameters %}
+{%- if param.kind|is_object_kind %}
+ payload_size += GetSerializedSize_(in_{{param.name}});
+{%- endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro build_message(params_name, parameters, params_description) -%}
+ {# TODO(yzshen): Consider refactoring to share code with
+ struct_serialization_definition.tmpl #}
+ {{params_name}}* params =
+ {{params_name}}::New(builder.buffer());
+{#--- Sets #}
+{% for param in parameters %}
+{%- if param.kind|is_object_kind %}
+{%- if param.kind|is_any_array_kind %}
+ mojo::SerializeArray_<{{param.kind|get_array_validate_params|indent(24)}}>(
+ mojo::internal::Forward(in_{{param.name}}), builder.buffer(), ¶ms->{{param.name}}.ptr);
+{%- else %}
+ Serialize_(mojo::internal::Forward(in_{{param.name}}), builder.buffer(), ¶ms->{{param.name}}.ptr);
+{%- endif %}
+{%- if not param.kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !params->{{param.name}}.ptr,
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null {{param.name}} argument in {{params_description}}");
+{%- endif %}
+{%- elif param.kind|is_any_handle_kind %}
+{%- if param.kind|is_interface_kind or
+ param.kind|is_interface_request_kind %}
+ // Delegate handle.
+ params->{{param.name}} = in_{{param.name}}.PassMessagePipe().release();
+{%- else %}
+ params->{{param.name}} = in_{{param.name}}.release();
+{%- endif %}
+{%- if not param.kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !params->{{param.name}}.is_valid(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ "invalid {{param.name}} argument in {{params_description}}");
+{%- endif %}
+{%- else %}
+ params->{{param.name}} = in_{{param.name}};
+{%- endif %}
+{%- endfor %}
+ mojo::Message message;
+ params->EncodePointersAndHandles(message.mutable_handles());
+ builder.Finish(&message);
+{%- endmacro %}
+
+{#--- Begin #}
+const char* {{class_name}}::Name_ = "{{namespace_as_string}}::{{class_name}}";
+{#--- Constants #}
+{% for constant in interface.constants %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant|constant_value}};
+{%- endfor %}
+
+{#--- ForwardToCallback definition #}
+{%- for method in interface.methods -%}
+{%- if method.response_parameters != None %}
+class {{class_name}}_{{method.name}}_ForwardToCallback
+ : public mojo::MessageReceiver {
+ public:
+ {{class_name}}_{{method.name}}_ForwardToCallback(
+ const {{interface_macros.declare_callback(method)}}& callback)
+ : callback_(callback) {
+ }
+ virtual bool Accept(mojo::Message* message) override;
+ private:
+ {{interface_macros.declare_callback(method)}} callback_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback);
+};
+bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept(
+ mojo::Message* message) {
+ internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
+ message->mutable_payload());
+
+ params->DecodePointersAndHandles(message->mutable_handles());
+ {{alloc_params(method.response_parameters)|indent(2)}}
+ callback_.Run({{pass_params(method.response_parameters)}});
+ return true;
+}
+{%- endif %}
+{%- endfor %}
+
+{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver)
+ : receiver_(receiver) {
+}
+
+{#--- Proxy definitions #}
+
+{%- for method in interface.methods %}
+{%- set message_name =
+ "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%- set params_name =
+ "internal::%s_%s_Params_Data"|format(interface.name, method.name) %}
+{%- set params_description =
+ "%s.%s request"|format(interface.name, method.name) %}
+void {{proxy_name}}::{{method.name}}(
+ {{interface_macros.declare_request_params("in_", method)}}) {
+ {{compute_payload_size(params_name, method.parameters)}}
+
+{%- if method.response_parameters != None %}
+ mojo::internal::RequestMessageBuilder builder({{message_name}}, payload_size);
+{%- else %}
+ mojo::internal::MessageBuilder builder({{message_name}}, payload_size);
+{%- endif %}
+
+ {{build_message(params_name, method.parameters, params_description)}}
+
+{%- if method.response_parameters != None %}
+ mojo::MessageReceiver* responder =
+ new {{class_name}}_{{method.name}}_ForwardToCallback(callback);
+ if (!receiver_->AcceptWithResponder(&message, responder))
+ delete responder;
+{%- else %}
+ bool ok MOJO_ALLOW_UNUSED = receiver_->Accept(&message);
+ // This return value may be ignored as !ok implies the Connector has
+ // encountered an error, which will be visible through other means.
+{%- endif %}
+}
+{%- endfor %}
+
+{#--- ProxyToResponder definition #}
+{%- for method in interface.methods -%}
+{%- if method.response_parameters != None %}
+{%- set message_name =
+ "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%- set params_name =
+ "internal::%s_%s_ResponseParams_Data"|format(interface.name, method.name) %}
+{%- set params_description =
+ "%s.%s response"|format(interface.name, method.name) %}
+class {{class_name}}_{{method.name}}_ProxyToResponder
+ : public {{interface_macros.declare_callback(method)}}::Runnable {
+ public:
+ virtual ~{{class_name}}_{{method.name}}_ProxyToResponder() {
+ delete responder_;
+ }
+
+ {{class_name}}_{{method.name}}_ProxyToResponder(
+ uint64_t request_id,
+ mojo::MessageReceiver* responder)
+ : request_id_(request_id),
+ responder_(responder) {
+ }
+
+ virtual void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const override;
+
+ private:
+ uint64_t request_id_;
+ mutable mojo::MessageReceiver* responder_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
+};
+void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
+ {{interface_macros.declare_params("in_", method.response_parameters)}}) const {
+ {{compute_payload_size(params_name, method.response_parameters)}}
+ mojo::internal::ResponseMessageBuilder builder(
+ {{message_name}}, payload_size, request_id_);
+ {{build_message(params_name, method.response_parameters, params_description)}}
+ bool ok MOJO_ALLOW_UNUSED = responder_->Accept(&message);
+ // TODO(darin): !ok returned here indicates a malformed message, and that may
+ // be good reason to close the connection. However, we don't have a way to do
+ // that from here. We should add a way.
+ delete responder_;
+ responder_ = nullptr;
+}
+{%- endif -%}
+{%- endfor %}
+
+{{class_name}}Stub::{{class_name}}Stub()
+ : sink_(nullptr) {
+}
+
+{#--- Stub definition #}
+
+bool {{class_name}}Stub::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters == None %}
+ internal::{{class_name}}_{{method.name}}_Params_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
+ message->mutable_payload());
+
+ params->DecodePointersAndHandles(message->mutable_handles());
+ {{alloc_params(method.parameters)|indent(6)}}
+ sink_->{{method.name}}({{pass_params(method.parameters)}});
+ return true;
+{%- else %}
+ break;
+{%- endif %}
+ }
+{%- endfor %}
+ }
+{%- endif %}
+ return false;
+}
+
+bool {{class_name}}Stub::AcceptWithResponder(
+ mojo::Message* message, mojo::MessageReceiver* responder) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters != None %}
+ internal::{{class_name}}_{{method.name}}_Params_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
+ message->mutable_payload());
+
+ params->DecodePointersAndHandles(message->mutable_handles());
+ {{interface_macros.declare_callback(method)}}::Runnable* runnable =
+ new {{class_name}}_{{method.name}}_ProxyToResponder(
+ message->request_id(), responder);
+ {{interface_macros.declare_callback(method)}} callback(runnable);
+ {{alloc_params(method.parameters)|indent(6)}}
+ sink_->{{method.name}}(
+{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback);
+ return true;
+{%- else %}
+ break;
+{%- endif %}
+ }
+{%- endfor %}
+ }
+{%- endif %}
+ return false;
+}
+
+{#--- Request validator definitions #}
+
+{{class_name}}RequestValidator::{{class_name}}RequestValidator(
+ mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
+bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters != None %}
+ if (!message->has_flag(mojo::internal::kMessageExpectsResponse))
+ break;
+{%- else %}
+ if (message->has_flag(mojo::internal::kMessageExpectsResponse) ||
+ message->has_flag(mojo::internal::kMessageIsResponse)) {
+ break;
+ }
+{%- endif %}
+ mojo::internal::BoundsChecker bounds_checker(
+ message->payload(), message->payload_num_bytes(),
+ message->handles()->size());
+ if (!internal::{{class_name}}_{{method.name}}_Params_Data::Validate(
+ message->payload(), &bounds_checker)) {
+ return false;
+ }
+ break;
+ }
+{%- endfor %}
+ }
+{%- endif %}
+
+ return sink_->Accept(message);
+}
+
+{#--- Response validator definitions #}
+{% if interface|has_callbacks %}
+{{class_name}}ResponseValidator::{{class_name}}ResponseValidator(
+ mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
+bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods if method.response_parameters != None %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+ if (!message->has_flag(mojo::internal::kMessageIsResponse))
+ break;
+ mojo::internal::BoundsChecker bounds_checker(
+ message->payload(), message->payload_num_bytes(),
+ message->handles()->size());
+ if (!internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate(
+ message->payload(), &bounds_checker)) {
+ return false;
+ }
+ break;
+ }
+{%- endfor %}
+ }
+{%- endif %}
+
+ return sink_->Accept(message);
+}
+{%- endif -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
new file mode 100644
index 0000000..fbefce2
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -0,0 +1,23 @@
+{%- macro declare_params(prefix, parameters) %}
+{%- for param in parameters -%}
+{{param.kind|cpp_const_wrapper_type}} {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro declare_callback(method) -%}
+mojo::Callback<void(
+{%- for param in method.response_parameters -%}
+{{param.kind|cpp_result_type}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor -%}
+)>
+{%- endmacro -%}
+
+{%- macro declare_request_params(prefix, method) -%}
+{{declare_params(prefix, method.parameters)}}
+{%- if method.response_parameters != None -%}
+{%- if method.parameters %}, {% endif %}
+const {{declare_callback(method)}}& callback
+{%- endif -%}
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
new file mode 100644
index 0000000..6b8e7c5
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -0,0 +1,14 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy : public {{interface.name}} {
+ public:
+ explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
+
+{%- for method in interface.methods %}
+ virtual void {{method.name}}(
+ {{interface_macros.declare_request_params("", method)}}
+ ) override;
+{%- endfor %}
+
+ private:
+ mojo::MessageReceiverWithResponder* receiver_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
new file mode 100644
index 0000000..2239b69
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
@@ -0,0 +1,6 @@
+class {{interface.name}}RequestValidator : public mojo::MessageFilter {
+ public:
+ explicit {{interface.name}}RequestValidator(mojo::MessageReceiver* sink = nullptr);
+
+ virtual bool Accept(mojo::Message* message) override;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
new file mode 100644
index 0000000..801603d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
@@ -0,0 +1,6 @@
+class {{interface.name}}ResponseValidator : public mojo::MessageFilter {
+ public:
+ explicit {{interface.name}}ResponseValidator(mojo::MessageReceiver* sink = nullptr);
+
+ virtual bool Accept(mojo::Message* message) override;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
new file mode 100644
index 0000000..afc6504
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -0,0 +1,13 @@
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponder {
+ public:
+ {{interface.name}}Stub();
+ void set_sink({{interface.name}}* sink) { sink_ = sink; }
+ {{interface.name}}* sink() { return sink_; }
+
+ virtual bool Accept(mojo::Message* message) override;
+ virtual bool AcceptWithResponder(mojo::Message* message,
+ mojo::MessageReceiver* responder) override;
+
+ private:
+ {{interface.name}}* sink_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
new file mode 100644
index 0000000..f0cf33b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
@@ -0,0 +1,49 @@
+// 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.
+
+{%- set header_guard = "%s_INTERNAL_H_"|
+ format(module.path|upper|replace("/","_")|replace(".","_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+
+{%- for import in imports %}
+#include "{{import.module.path}}-internal.h"
+{%- endfor %}
+
+namespace mojo {
+namespace internal {
+class BoundsChecker;
+}
+}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Wrapper forward declarations #}
+{% for struct in structs %}
+class {{struct.name}};
+{%- endfor %}
+
+namespace internal {
+
+#pragma pack(push, 1)
+
+{#--- Class declarations #}
+{% for struct in structs %}
+{% include "struct_declaration.tmpl" %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+} // namespace internal
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#endif // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
new file mode 100644
index 0000000..8f5e812
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -0,0 +1,94 @@
+// 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.
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+#elif defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4056)
+#pragma warning(disable:4756)
+#endif
+
+#include "{{module.path}}.h"
+
+#include <math.h>
+
+#include "mojo/public/cpp/bindings/lib/array_serialization.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/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Constants #}
+{% for constant in module.constants %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%- endfor %}
+
+namespace internal {
+namespace {
+
+#pragma pack(push, 1)
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
+const uint32_t {{method_name}} = {{method.ordinal}};
+{% set struct = method|struct_from_method %}
+{%- include "params_definition.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method|response_struct_from_method %}
+{%- include "params_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+} // namespace
+
+{#--- Struct definitions #}
+{% for struct in structs %}
+{%- include "struct_definition.tmpl" %}
+{%- endfor %}
+
+} // namespace internal
+
+{#--- Struct Constants #}
+{%- for struct in structs %}
+{% for constant in struct.constants %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant|constant_value}};
+{%- endfor %}
+{%- endfor %}
+
+{#--- Struct builder definitions #}
+{%- for struct in structs %}
+{%- include "wrapper_class_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces %}
+{%- include "interface_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers #}
+{%- for struct in structs %}
+{%- include "struct_serialization_definition.tmpl" %}
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning(pop)
+#endif
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
new file mode 100644
index 0000000..4e21d47
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -0,0 +1,108 @@
+// 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.
+
+{%- set header_guard = "%s_H_"|
+ format(module.path|upper|replace("/","_")|replace(".","_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/no_interface.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "{{module.path}}-internal.h"
+{%- for import in imports %}
+#include "{{import.module.path}}.h"
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Constants #}
+{% for constant in module.constants %}
+extern const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+
+{#--- Enums #}
+{% for enum in enums %}
+{% include "enum_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Forward Declarations -#}
+{% for interface in interfaces %}
+class {{interface.name}};
+typedef mojo::InterfacePtr<{{interface.name}}> {{interface.name}}Ptr;
+{% endfor %}
+
+{#--- Struct Forward Declarations -#}
+{% for struct in structs %}
+class {{struct.name}};
+{% if struct|should_inline %}
+typedef mojo::InlinedStructPtr<{{struct.name}}> {{struct.name}}Ptr;
+{% else %}
+typedef mojo::StructPtr<{{struct.name}}> {{struct.name}}Ptr;
+{% endif %}
+{% endfor %}
+
+{#--- NOTE: Non-inlined structs may have pointers to inlined structs, so we #}
+{#--- need to fully define inlined structs ahead of the others. #}
+
+{#--- Inlined structs #}
+{% for struct in structs %}
+{% if struct|should_inline %}
+{% include "wrapper_class_declaration.tmpl" %}
+{% endif %}
+{%- endfor %}
+
+{#--- Non-inlined structs #}
+{% for struct in structs %}
+{% if not struct|should_inline %}
+{% include "wrapper_class_declaration.tmpl" %}
+{% endif %}
+{%- endfor %}
+
+{#--- Interfaces -#}
+{% for interface in interfaces %}
+{% include "interface_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Proxies -#}
+{% for interface in interfaces %}
+{% include "interface_proxy_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Stubs -#}
+{% for interface in interfaces %}
+{% include "interface_stub_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Request Validators -#}
+{% for interface in interfaces %}
+{% include "interface_request_validator_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Response Validators -#}
+{% for interface in interfaces if interface|has_callbacks %}
+{% include "interface_response_validator_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers -#}
+{% if structs %}
+{% for struct in structs %}
+{% include "struct_serialization_declaration.tmpl" %}
+{%- endfor %}
+{%- endif %}
+
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#endif // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl
new file mode 100644
index 0000000..0b11047
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl
@@ -0,0 +1,33 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+class {{class_name}} {
+ public:
+ static {{class_name}}* New(mojo::internal::Buffer* buf) {
+ return new (buf->Allocate(sizeof({{class_name}})))
+ {{class_name}}();
+ }
+
+ static bool Validate(const void* data,
+ mojo::internal::BoundsChecker* bounds_checker) {
+ {{ struct_macros.validate(struct, class_name)|indent(4) }}
+ }
+
+ mojo::internal::StructHeader header_;
+{{struct_macros.fields(struct)}}
+
+ void EncodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.encodes(struct)|indent(4) }}
+ }
+
+ void DecodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.decodes(struct)|indent(4) }}
+ }
+
+ private:
+ {{class_name}}() {
+ header_.num_bytes = sizeof(*this);
+ header_.num_fields = {{struct.packed.packed_fields|length}};
+ }
+};
+MOJO_COMPILE_ASSERT(sizeof({{class_name}}) == {{struct.packed|struct_size}},
+ bad_sizeof_{{class_name}});
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
new file mode 100644
index 0000000..60a6a9e
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -0,0 +1,22 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" -%}
+
+class {{class_name}} {
+ public:
+ static {{class_name}}* New(mojo::internal::Buffer* buf);
+
+ static bool Validate(const void* data,
+ mojo::internal::BoundsChecker* bounds_checker);
+
+ mojo::internal::StructHeader header_;
+{{struct_macros.fields(struct)}}
+
+ void EncodePointersAndHandles(std::vector<mojo::Handle>* handles);
+ void DecodePointersAndHandles(std::vector<mojo::Handle>* handles);
+
+ private:
+ {{class_name}}();
+ ~{{class_name}}(); // NOT IMPLEMENTED
+};
+MOJO_COMPILE_ASSERT(sizeof({{class_name}}) == {{struct.packed|struct_size}},
+ bad_sizeof_{{class_name}});
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
new file mode 100644
index 0000000..461f158
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -0,0 +1,28 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+
+// static
+{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) {
+ return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
+}
+
+// static
+bool {{class_name}}::Validate(const void* data,
+ mojo::internal::BoundsChecker* bounds_checker) {
+ {{ struct_macros.validate(struct, class_name)|indent(2) }}
+}
+
+{{class_name}}::{{class_name}}() {
+ header_.num_bytes = sizeof(*this);
+ header_.num_fields = {{struct.packed.packed_fields|length}};
+}
+
+void {{class_name}}::EncodePointersAndHandles(
+ std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.encodes(struct)|indent(2) }}
+}
+
+void {{class_name}}::DecodePointersAndHandles(
+ std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.decodes(struct)|indent(2) }}
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
new file mode 100644
index 0000000..0e51193
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -0,0 +1,115 @@
+{%- macro validate(struct, class_name) %}
+ if (!data)
+ return true;
+
+ if (!ValidateStructHeader(
+ data, sizeof({{class_name}}),
+ {{struct.packed.packed_fields|length}}, bounds_checker)) {
+ return false;
+ }
+
+ const {{class_name}}* MOJO_ALLOW_UNUSED object =
+ static_cast<const {{class_name}}*>(data);
+
+{%- for packed_field in struct.packed.packed_fields %}
+{%- set name = packed_field.field.name %}
+{%- set kind = packed_field.field.kind %}
+{%- if kind|is_object_kind %}
+{%- set wrapper_type = kind|cpp_wrapper_type %}
+{%- if not kind|is_nullable_kind %}
+ if (!object->{{name}}.offset) {
+ ReportValidationError(
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null {{name}} field in {{struct.name}} struct");
+ return false;
+ }
+{%- endif %}
+ if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) {
+ ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER);
+ return false;
+ }
+{%- if kind|is_any_array_kind or kind|is_string_kind %}
+ if (!{{wrapper_type}}::Data_::Validate<
+ {{kind|get_array_validate_params|indent(10)}}>(
+ mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
+ bounds_checker)) {
+{%- else %}
+ if (!{{wrapper_type}}::Data_::Validate(
+ mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
+ bounds_checker)) {
+{%- endif %}
+ return false;
+ }
+{%- elif kind|is_any_handle_kind %}
+{%- if not kind|is_nullable_kind %}
+ if (object->{{name}}.value() == mojo::internal::kEncodedInvalidHandleValue) {
+ ReportValidationError(
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ "invalid {{name}} field in {{struct.name}} struct");
+ return false;
+ }
+{%- endif %}
+ if (!bounds_checker->ClaimHandle(object->{{name}})) {
+ ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE);
+ return false;
+ }
+{%- endif %}
+{%- endfor %}
+
+ return true;
+{%- endmacro %}
+
+{%- macro field_line(field) %}
+{%- set type = field.kind|cpp_field_type %}
+{%- set name = field.name -%}
+{%- if field.kind.spec == 'b' -%}
+ uint8_t {{name}} : 1;
+{%- elif field.kind|is_enum_kind -%}
+ int32_t {{name}};
+{%- else -%}
+ {{type}} {{name}};
+{%- endif %}
+{%- endmacro %}
+
+{%- macro fields(struct) %}
+{%- for packed_field in struct.packed.packed_fields %}
+ {{field_line(packed_field.field)}}
+{%- if not loop.last %}
+{%- set next_pf = struct.packed.packed_fields[loop.index0 + 1] %}
+{%- set pad = next_pf.offset - (packed_field.offset + packed_field.size) %}
+{%- if pad > 0 %}
+ uint8_t pad{{loop.index0}}_[{{pad}}];
+{%- endif %}
+{%- endif %}
+{%- endfor -%}
+
+{%- set num_fields = struct.packed.packed_fields|length %}
+{%- if num_fields > 0 %}
+{%- set last_field = struct.packed.packed_fields[num_fields - 1] %}
+{%- set offset = last_field.offset + last_field.size %}
+{%- set pad = offset|get_pad(8) -%}
+{%- if pad > 0 %}
+ uint8_t padfinal_[{{pad}}];
+{%- endif %}
+{%- endif %}
+{%- endmacro %}
+
+{%- macro encodes(struct) -%}
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+mojo::internal::Encode(&{{pf.field.name}}, handles);
+{%- elif pf.field.kind|is_any_handle_kind %}
+mojo::internal::EncodeHandle(&{{pf.field.name}}, handles);
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+{%- macro decodes(struct) -%}
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+mojo::internal::Decode(&{{pf.field.name}}, handles);
+{%- elif pf.field.kind|is_any_handle_kind %}
+mojo::internal::DecodeHandle(&{{pf.field.name}}, handles);
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
new file mode 100644
index 0000000..604be86
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -0,0 +1,5 @@
+size_t GetSerializedSize_(const {{struct.name}}Ptr& input);
+void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buffer,
+ internal::{{struct.name}}_Data** output);
+void Deserialize_(internal::{{struct.name}}_Data* input,
+ {{struct.name}}Ptr* output);
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
new file mode 100644
index 0000000..09bf392
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -0,0 +1,75 @@
+size_t GetSerializedSize_(const {{struct.name}}Ptr& input) {
+ if (!input)
+ return 0;
+ size_t size = sizeof(internal::{{struct.name}}_Data);
+{%- for pf in struct.packed.packed_fields if pf.field.kind|is_object_kind %}
+ size += GetSerializedSize_(input->{{pf.field.name}});
+{%- endfor %}
+ return size;
+}
+
+void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buf,
+ internal::{{struct.name}}_Data** output) {
+ if (input) {
+ internal::{{struct.name}}_Data* result =
+ internal::{{struct.name}}_Data::New(buf);
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+{%- if pf.field.kind|is_any_array_kind %}
+ mojo::SerializeArray_<{{pf.field.kind|get_array_validate_params|indent(26)}}>(
+ mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr);
+{%- else %}
+ Serialize_(mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr);
+{%- endif %}
+{%- if not pf.field.kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !result->{{pf.field.name}}.ptr,
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null {{pf.field.name}} field in {{struct.name}} struct");
+{%- endif %}
+{%- elif pf.field.kind|is_any_handle_kind %}
+{%- if pf.field.kind|is_interface_kind %}
+ result->{{pf.field.name}} = input->{{pf.field.name}}.PassMessagePipe().release();
+{%- else %}
+ result->{{pf.field.name}} = input->{{pf.field.name}}.release();
+{%- endif %}
+{%- if not pf.field.kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !result->{{pf.field.name}}.is_valid(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ "invalid {{pf.field.name}} field in {{struct.name}} struct");
+{%- endif %}
+{%- else %}
+ result->{{pf.field.name}} = input->{{pf.field.name}};
+{%- endif %}
+{%- endfor %}
+ *output = result;
+ } else {
+ *output = nullptr;
+ }
+}
+
+void Deserialize_(internal::{{struct.name}}_Data* input,
+ {{struct.name}}Ptr* output) {
+ if (input) {
+ {{struct.name}}Ptr result({{struct.name}}::New());
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+ Deserialize_(input->{{pf.field.name}}.ptr, &result->{{pf.field.name}});
+{%- elif pf.field.kind|is_interface_kind %}
+ if (input->{{pf.field.name}}.is_valid())
+ result->{{pf.field.name}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&input->{{pf.field.name}})));
+{%- elif pf.field.kind|is_any_handle_kind %}
+ result->{{pf.field.name}}.reset(mojo::internal::FetchAndReset(&input->{{pf.field.name}}));
+{%- elif pf.field.kind|is_enum_kind %}
+ result->{{pf.field.name}} = static_cast<{{pf.field.kind|cpp_wrapper_type}}>(
+ input->{{pf.field.name}});
+{%- else %}
+ result->{{pf.field.name}} = input->{{pf.field.name}};
+{%- endif %}
+{%- endfor %}
+ *output = result.Pass();
+ } else {
+ output->reset();
+ }
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
new file mode 100644
index 0000000..21f2968
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -0,0 +1,34 @@
+
+class {{struct.name}} {
+ public:
+ typedef internal::{{struct.name}}_Data Data_;
+
+{#--- Constants #}
+{%- for constant in struct.constants %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+{% macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
+ {{enum_def()|indent(2)}}
+{%- endfor %}
+ static {{struct.name}}Ptr New();
+
+ template <typename U>
+ static {{struct.name}}Ptr From(const U& u) {
+ return mojo::TypeConverter<{{struct.name}}Ptr, U>::Convert(u);
+ }
+
+ {{struct.name}}();
+ ~{{struct.name}}();
+{% if struct|is_cloneable_kind %}
+ {{struct.name}}Ptr Clone() const;
+{%- endif %}
+
+{#--- Getters #}
+{% for field in struct.fields %}
+{%- set type = field.kind|cpp_wrapper_type %}
+{%- set name = field.name %}
+ {{type}} {{name}};
+{%- endfor %}
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
new file mode 100644
index 0000000..42f7575
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -0,0 +1,28 @@
+// static
+{{struct.name}}Ptr {{struct.name}}::New() {
+ {{struct.name}}Ptr rv;
+ mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv);
+ return rv.Pass();
+}
+
+{{struct.name}}::{{struct.name}}()
+{%- for field in struct.fields %}
+ {% if loop.first %}:{% else %} {% endif %} {{field.name}}({{field|default_value}}){% if not loop.last %},{% endif %}
+{%- endfor %} {
+}
+
+{{struct.name}}::~{{struct.name}}() {
+}
+{% if struct|is_cloneable_kind %}
+{{struct.name}}Ptr {{struct.name}}::Clone() const {
+ {{struct.name}}Ptr rv(New());
+{%- for field in struct.fields %}
+{%- if field.kind|is_struct_kind or field.kind|is_any_array_kind %}
+ rv->{{field.name}} = {{field.name}}.Clone();
+{%- else %}
+ rv->{{field.name}} = {{field.name}};
+{%- endif %}
+{%- endfor %}
+ return rv.Pass();
+}
+{% endif %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl
new file mode 100644
index 0000000..db193e2
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl
@@ -0,0 +1,3 @@
+{% macro constant_def(constant) %}
+public static final {{constant.kind|java_type}} {{constant|name}} = {{constant|constant_value}};
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl
new file mode 100644
index 0000000..0a4e299
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl
@@ -0,0 +1,12 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% include "header.java.tmpl" %}
+
+public final class {{main_entity}} {
+{% for constant in constants %}
+
+ {{constant_def(constant)|indent(4)}}
+{% endfor %}
+
+ private {{main_entity}}() {}
+
+}
diff --git a/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl
new file mode 100644
index 0000000..7096a18
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl
@@ -0,0 +1,4 @@
+{% from "enum_definition.tmpl" import enum_def %}
+{% include "header.java.tmpl" %}
+
+{{enum_def(enum, true)}}
diff --git a/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
new file mode 100644
index 0000000..a16c178
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
@@ -0,0 +1,21 @@
+{%- macro enum_value(enum, field, index) -%}
+{%- if field.value -%}
+(int) ({{field.value|expression_to_text('i32')}})
+{%- elif index == 0 -%}
+0
+{%- else -%}
+{{enum.fields[index - 1]|name}} + 1
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro enum_def(enum, top_level) -%}
+public {{ 'static ' if not top_level }}final class {{enum|name}} {
+
+{% for field in enum.fields %}
+ public static final int {{field|name}} = {{enum_value(enum, field, loop.index0)}};
+{% endfor %}
+
+ private {{enum|name}}() {}
+
+}
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
new file mode 100644
index 0000000..ec6a88b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
@@ -0,0 +1,11 @@
+// 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 is autogenerated by:
+// mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+// {{module.path}}
+//
+
+package {{package}};
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl
new file mode 100644
index 0000000..10e5d7a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl
@@ -0,0 +1,4 @@
+{% from "interface_definition.tmpl" import interface_def %}
+{% include "header.java.tmpl" %}
+
+{{ interface_def(interface, client) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
new file mode 100644
index 0000000..941fec7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
@@ -0,0 +1,294 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+{% from "struct_definition.tmpl" import struct_def %}
+
+{%- macro declare_params(parameters, boxed=false) %}
+{%- for param in parameters -%}
+{{param.kind|java_type(boxed)}} {{param|name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{% macro declare_request_params(method) %}
+{{declare_params(method.parameters)}}
+{%- if method.response_parameters != None -%}
+{%- if method.parameters %}, {% endif %}
+{{method|interface_response_name}} callback
+{%- endif -%}
+{% endmacro %}
+
+{%- macro declare_callback(method) -%}
+
+interface {{method|interface_response_name}} extends org.chromium.mojo.bindings.Callbacks.Callback{{method.response_parameters|length}}{% if method.response_parameters %}<
+{%- for param in method.response_parameters -%}
+{{param.kind|java_type(True)}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor -%}
+>{% endif %} { }
+{%- endmacro -%}
+
+{%- macro run_callback(variable, parameters) -%}
+{%- if parameters -%}
+{%- for param in parameters -%}
+{{variable}}.{{param|name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor -%}
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro super_class(client, with_generic=True) -%}
+{%- if client -%}
+org.chromium.mojo.bindings.InterfaceWithClient{% if with_generic %}<{{client|java_type}}>{% endif %}
+{%- else -%}
+org.chromium.mojo.bindings.Interface
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro flags_for_method(method, is_parameter) -%}
+{{flags(method.response_parameters, is_parameter)}}
+{%- endmacro -%}
+
+{%- macro flags(has_response_parameters, is_parameter) -%}
+{%- if not has_response_parameters -%}
+org.chromium.mojo.bindings.MessageHeader.NO_FLAG
+{%- elif is_parameter: -%}
+org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG
+{%- else -%}
+org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro manager_class(interface, client, fully_qualified=False) -%}
+{% if fully_qualified %}{{super_class(client, False)}}.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy
+{%- if client -%}, {{client|java_type}}{%- endif -%}
+>
+{%- endmacro -%}
+
+{%- macro manager_def(interface, client) -%}
+public static final {{manager_class(interface, client, True)}} MANAGER =
+ new {{manager_class(interface, client, True)}}() {
+
+ public String getName() {
+ return "{{namespace|replace(".","::")}}::{{interface.name}}";
+ }
+
+ public Proxy buildProxy(org.chromium.mojo.system.Core core,
+ org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) {
+ return new Proxy(core, messageReceiver);
+ }
+
+ public Stub buildStub(org.chromium.mojo.system.Core core, {{interface|name}} impl) {
+ return new Stub(core, impl);
+ }
+
+ public {{interface|name}}[] buildArray(int size) {
+ return new {{interface|name}}[size];
+ }
+{% if client %}
+
+ protected org.chromium.mojo.bindings.Interface.Manager<{{client|java_type}}, ?> getClientManager() {
+ return {{client|java_type}}.MANAGER;
+ }
+{% endif %}
+};
+{%- endmacro -%}
+
+{%- macro accept_body(interface, with_response) -%}
+{% if (interface|has_method_with_response and with_response) or
+ (interface|has_method_without_response and not with_response) %}
+try {
+ org.chromium.mojo.bindings.ServiceMessage messageWithHeader =
+ message.asServiceMessage();
+ org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader();
+ if (!header.validateHeader({{flags(with_response, True)}})) {
+ return false;
+ }
+ switch(header.getType()) {
+{% for method in interface.methods %}
+{% if (with_response and method.response_parameters != None) or
+ (not with_response and method.response_parameters == None) %}
+{% set request_struct = method|struct_from_method %}
+{% if with_response %}
+{% set response_struct = method|response_struct_from_method %}
+{% endif %}
+ case {{method|method_ordinal_name}}: {
+{% if method.parameters %}
+ {{request_struct|name}} data =
+ {{request_struct|name}}.deserialize(messageWithHeader.getPayload());
+{% else %}
+ {{request_struct|name}}.deserialize(messageWithHeader.getPayload());
+{% endif %}
+ getImpl().{{method|name}}({{run_callback('data', method.parameters)}}{% if with_response %}{% if method.parameters %}, {% endif %}new {{response_struct|name}}ProxyToResponder(getCore(), receiver, header.getRequestId()){% endif %});
+ return true;
+ }
+{% endif %}
+{% endfor %}
+ default:
+ return false;
+ }
+} catch (org.chromium.mojo.bindings.DeserializationException e) {
+ return false;
+}
+{% else %}
+return false;
+{% endif %}
+{%- endmacro -%}
+
+{% macro interface_def(interface, client) %}
+public interface {{interface|name}} extends {{super_class(client)}} {
+{% for constant in interface.constants %}
+
+ {{constant_def(constant)|indent(4)}}
+{% endfor %}
+{% for enum in interface.enums %}
+
+ {{enum_def(enum, false)|indent(4)}}
+{% endfor %}
+
+ public interface Proxy extends {{interface|name}}, {{super_class(client, False)}}.Proxy{% if client %}<{{client|java_type}}>{% endif %} {
+ }
+
+ {{manager_class(interface, client)}} MANAGER = {{interface|name}}_Internal.MANAGER;
+{% for method in interface.methods %}
+
+ void {{method|name}}({{declare_request_params(method)}});
+{% if method.response_parameters != None %}
+ {{declare_callback(method)|indent(4)}}
+{% endif %}
+{% endfor %}
+}
+{% endmacro %}
+
+{% macro interface_internal_def(interface, client) %}
+class {{interface|name}}_Internal {
+
+ {{manager_def(interface, client)|indent(4)}}
+
+{% for method in interface.methods %}
+ private static final int {{method|method_ordinal_name}} = {{method.ordinal}};
+{% endfor %}
+
+ static final class Proxy extends {% if client %}org.chromium.mojo.bindings.InterfaceWithClient.AbstractProxy<{{client|java_type}}>{% else %}org.chromium.mojo.bindings.Interface.AbstractProxy{% endif %} implements {{interface|name}}.Proxy {
+
+ Proxy(org.chromium.mojo.system.Core core,
+ org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) {
+ super(core, messageReceiver);
+ }
+{% for method in interface.methods %}
+
+ @Override
+ public void {{method|name}}({{declare_request_params(method)}}) {
+{% set request_struct = method|struct_from_method %}
+ {{request_struct|name}} _message = new {{request_struct|name}}();
+{% for param in method.parameters %}
+ _message.{{param|name}} = {{param|name}};
+{% endfor %}
+{% if method.response_parameters != None %}
+ getMessageReceiver().acceptWithResponder(
+ _message.serializeWithHeader(
+ getCore(),
+ new org.chromium.mojo.bindings.MessageHeader(
+ {{method|method_ordinal_name}},
+ {{flags_for_method(method, True)}},
+ 0)),
+ new {{method|response_struct_from_method|name}}ForwardToCallback(callback));
+{% else %}
+ getMessageReceiver().accept(
+ _message.serializeWithHeader(
+ getCore(),
+ new org.chromium.mojo.bindings.MessageHeader({{method|method_ordinal_name}})));
+{% endif %}
+ }
+{% endfor %}
+
+ }
+
+ static final class Stub extends org.chromium.mojo.bindings.Interface.Stub<{{interface|name}}> {
+
+ Stub(org.chromium.mojo.system.Core core, {{interface|name}} impl) {
+ super(core, impl);
+ }
+
+ @Override
+ public boolean accept(org.chromium.mojo.bindings.Message message) {
+ {{accept_body(interface, False)|indent(12)}}
+ }
+
+ @Override
+ public boolean acceptWithResponder(org.chromium.mojo.bindings.Message message, org.chromium.mojo.bindings.MessageReceiver receiver) {
+ {{accept_body(interface, True)|indent(12)}}
+ }
+ }
+{% for method in interface.methods %}
+
+ {{ struct_def(method|struct_from_method, True)|indent(4) }}
+{% if method.response_parameters != None %}
+{% set response_struct = method|response_struct_from_method %}
+
+ {{ struct_def(response_struct, True)|indent(4) }}
+
+ static class {{response_struct|name}}ForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable
+ implements org.chromium.mojo.bindings.MessageReceiver {
+ private final {{interface|name}}.{{method|interface_response_name}} mCallback;
+
+ {{response_struct|name}}ForwardToCallback({{interface|name}}.{{method|interface_response_name}} callback) {
+ this.mCallback = callback;
+ }
+
+ @Override
+ public boolean accept(org.chromium.mojo.bindings.Message message) {
+ try {
+ org.chromium.mojo.bindings.ServiceMessage messageWithHeader =
+ message.asServiceMessage();
+ org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader();
+ if (!header.validateHeader({{method|method_ordinal_name}},
+ {{flags_for_method(method, False)}})) {
+ return false;
+ }
+{% if method.response_parameters|length %}
+ {{response_struct|name}} response = {{response_struct|name}}.deserialize(messageWithHeader.getPayload());
+{% endif %}
+ mCallback.call({{run_callback('response', method.response_parameters)}});
+ return true;
+ } catch (org.chromium.mojo.bindings.DeserializationException e) {
+ return false;
+ }
+ }
+ }
+
+ static class {{response_struct|name}}ProxyToResponder implements {{interface|name}}.{{method|interface_response_name}} {
+
+ private final org.chromium.mojo.system.Core mCore;
+ private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver;
+ private final long mRequestId;
+
+ {{response_struct|name}}ProxyToResponder(
+ org.chromium.mojo.system.Core core,
+ org.chromium.mojo.bindings.MessageReceiver messageReceiver,
+ long requestId) {
+ mCore = core;
+ mMessageReceiver = messageReceiver;
+ mRequestId = requestId;
+ }
+
+ @Override
+ public void call({{declare_params(method.response_parameters, true)}}) {
+ {{response_struct|name}} _response = new {{response_struct|name}}();
+{% for param in method.response_parameters %}
+ _response.{{param|name}} = {{param|name}};
+{% endfor %}
+ org.chromium.mojo.bindings.ServiceMessage _message =
+ _response.serializeWithHeader(
+ mCore,
+ new org.chromium.mojo.bindings.MessageHeader(
+ {{method|method_ordinal_name}},
+ {{flags_for_method(method, False)}},
+ mRequestId));
+ mMessageReceiver.accept(_message);
+ }
+ }
+{% endif %}
+{% endfor %}
+
+}
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl
new file mode 100644
index 0000000..efb17f3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl
@@ -0,0 +1,4 @@
+{% from "interface_definition.tmpl" import interface_internal_def %}
+{% include "header.java.tmpl" %}
+
+{{ interface_internal_def(interface, client) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl
new file mode 100644
index 0000000..232ec26
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl
@@ -0,0 +1,4 @@
+{% from "struct_definition.tmpl" import struct_def %}
+{% include "header.java.tmpl" %}
+
+{{ struct_def(struct) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl
new file mode 100644
index 0000000..2fc4439
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl
@@ -0,0 +1,118 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+
+{%- macro array_expected_length(kind) -%}
+{%- if kind|is_fixed_array_kind -%}
+{{kind.length}}
+{%- else -%}
+org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH
+{%- endif -%}
+{%- endmacro -%}
+
+{% macro encode(variable, kind, offset, bit, level=0) %}
+{% if kind|is_pointer_array_kind %}
+{% set sub_kind = kind.kind %}
+if ({{variable}} == null) {
+ encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
+} else {
+ org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.encodePointerArray({{variable}}.length, {{offset}}, {{array_expected_length(kind)}});
+ for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
+ {{encode(variable~'[i'~level~']', sub_kind, 'DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE * i'~level, 0, level+1)|indent(8)}}
+ }
+}
+{% else %}
+encoder{{level}}.{{kind|encode_method(variable, offset, bit)}};
+{% endif %}
+{% endmacro %}
+
+{% macro decode(variable, kind, offset, bit, level=0) %}
+{% if kind|is_struct_kind or kind|is_pointer_array_kind %}
+org.chromium.mojo.bindings.Decoder decoder{{level+1}} = decoder{{level}}.readPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
+{% if kind|is_struct_kind %}
+{{variable}} = {{kind|java_type}}.decode(decoder{{level+1}});
+{% else %}{# kind|is_pointer_array_kind #}
+if (decoder{{level+1}} == null) {
+ {{variable}} = null;
+} else {
+ DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{array_expected_length(kind)}});
+ {{variable}} = {{kind|new_array('si'~(level+1)~'.numFields')}};
+ for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.numFields; ++i{{level+1}}) {
+ {{decode(variable~'[i'~(level+1)~']', kind.kind, 'DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE * i'~(level+1), 0, level+1)|indent(8)}}
+ }
+}
+{% endif %}
+{% else %}
+{{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}};
+{% endif %}
+{% endmacro %}
+
+{% macro struct_def(struct, inner_class=False) %}
+{{'static' if inner_class else 'public'}} final class {{struct|name}} extends org.chromium.mojo.bindings.Struct {
+
+ private static final int STRUCT_SIZE = {{struct.packed|struct_size}};
+ private static final DataHeader DEFAULT_STRUCT_INFO = new DataHeader(STRUCT_SIZE, {{struct.packed.packed_fields|length}});
+{% for constant in struct.constants %}
+
+ {{constant_def(constant)|indent(4)}}
+{% endfor %}
+{% for enum in struct.enums %}
+
+ {{enum_def(enum, false)|indent(4)}}
+{% endfor %}
+{% if struct.fields %}
+
+{% for field in struct.fields %}
+ public {{field.kind|java_type}} {{field|name}};
+{% endfor %}
+{% endif %}
+
+ public {{struct|name}}() {
+ super(STRUCT_SIZE);
+{% for field in struct.fields %}
+{% if field.default %}
+ {{field|name}} = {{field|default_value}};
+{% elif field.kind|is_handle %}
+ {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE;
+{% endif %}
+{% endfor %}
+ }
+
+ public static {{struct|name}} deserialize(org.chromium.mojo.bindings.Message message) {
+ return decode(new org.chromium.mojo.bindings.Decoder(message));
+ }
+
+ public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) {
+ if (decoder0 == null) {
+ return null;
+ }
+ {{struct|name}} result = new {{struct|name}}();
+{% if not struct.bytes %}
+ decoder0.readDataHeader();
+{% else %}
+ DataHeader mainDataHeader = decoder0.readDataHeader();
+{% endif %}
+{% for byte in struct.bytes %}
+{% for packed_field in byte.packed_fields %}
+ if (mainDataHeader.numFields > {{packed_field.ordinal}}) {
+ {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(12)}}
+ }
+{% endfor %}
+{% endfor %}
+ return result;
+ }
+
+ @Override
+ protected final void encode(org.chromium.mojo.bindings.Encoder encoder) {
+{% if not struct.bytes %}
+ encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
+{% else %}
+ org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
+{% endif %}
+{% for byte in struct.bytes %}
+{% for packed_field in byte.packed_fields %}
+ {{encode(packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(8)}}
+{% endfor %}
+{% endfor %}
+ }
+}
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
new file mode 100644
index 0000000..795116d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -0,0 +1,14 @@
+{%- macro enum_def(enum_name, enum, module) -%}
+ {{enum_name}} = {};
+
+{%- set prev_enum = 0 %}
+{%- for field in enum.fields %}
+{%- if field.value %}
+ {{enum_name}}.{{field.name}} = {{field.value|expression_to_text}};
+{%- elif loop.first %}
+ {{enum_name}}.{{field.name}} = 0;
+{%- else %}
+ {{enum_name}}.{{field.name}} = {{enum_name}}.{{enum.fields[loop.index0 - 1].name}} + 1;
+{%- endif %}
+{%- endfor %}
+{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
new file mode 100644
index 0000000..b41929c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -0,0 +1,178 @@
+{%- set namespace_as_string = namespace|replace(".","::") %}
+{%- for method in interface.methods %}
+ var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
+{%- endfor %}
+
+ function {{interface.name}}Proxy(receiver) {
+ this.receiver_ = receiver;
+ }
+
+ {{interface.name}}Proxy.NAME_ = '{{namespace_as_string}}::{{interface.name}}';
+
+{%- for method in interface.methods %}
+ {{interface.name}}Proxy.prototype.{{method.name|stylize_method}} = function(
+{%- for parameter in method.parameters -%}
+{{parameter.name}}{% if not loop.last %}, {% endif %}
+{%- endfor -%}
+) {
+ var params = new {{interface.name}}_{{method.name}}_Params();
+{%- for parameter in method.parameters %}
+ params.{{parameter.name}} = {{parameter.name}};
+{%- endfor %}
+
+{%- if method.response_parameters == None %}
+ var builder = new codec.MessageBuilder(
+ k{{interface.name}}_{{method.name}}_Name,
+ codec.align({{interface.name}}_{{method.name}}_Params.encodedSize));
+ builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
+ var message = builder.finish();
+ this.receiver_.accept(message);
+{%- else %}
+ return new Promise(function(resolve, reject) {
+ var builder = new codec.MessageWithRequestIDBuilder(
+ k{{interface.name}}_{{method.name}}_Name,
+ codec.align({{interface.name}}_{{method.name}}_Params.encodedSize),
+ codec.kMessageExpectsResponse, 0);
+ builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
+ var message = builder.finish();
+ this.receiver_.acceptWithResponder(message, {
+ accept: function(message) {
+ var reader = new codec.MessageReader(message);
+ var responseParams =
+ reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
+ resolve(responseParams);
+ },
+ reject: function(result) {
+ reject(Error("Connection error: " + result));
+ },
+ }).catch(reject);
+ }.bind(this));
+{%- endif %}
+ };
+{%- endfor %}
+
+ function {{interface.name}}Stub() {
+ }
+
+ {{interface.name}}Stub.NAME_ = '{{namespace_as_string}}::{{interface.name}}';
+
+ {{interface.name}}Stub.prototype.accept = function(message) {
+ var reader = new codec.MessageReader(message);
+ switch (reader.messageName) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters == None %}
+ case k{{interface.name}}_{{method.name}}_Name:
+ var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params);
+ this.{{method.name|stylize_method}}(
+{%- for parameter in method.parameters -%}
+params.{{parameter.name}}{% if not loop.last %}, {% endif %}
+{%- endfor %});
+ return true;
+{%- endif %}
+{%- endfor %}
+ default:
+ return false;
+ }
+ };
+
+ {{interface.name}}Stub.prototype.acceptWithResponder =
+ function(message, responder) {
+ var reader = new codec.MessageReader(message);
+ switch (reader.messageName) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters != None %}
+ case k{{interface.name}}_{{method.name}}_Name:
+ var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params);
+ return this.{{method.name|stylize_method}}(
+{%- for parameter in method.parameters -%}
+params.{{parameter.name}}{% if not loop.last %}, {% endif -%}
+{%- endfor %}).then(function(response) {
+ var responseParams =
+ new {{interface.name}}_{{method.name}}_ResponseParams();
+{%- for parameter in method.response_parameters %}
+ responseParams.{{parameter.name}} = response.{{parameter.name}};
+{%- endfor %}
+ var builder = new codec.MessageWithRequestIDBuilder(
+ k{{interface.name}}_{{method.name}}_Name,
+ codec.align({{interface.name}}_{{method.name}}_ResponseParams.encodedSize),
+ codec.kMessageIsResponse, reader.requestID);
+ builder.encodeStruct({{interface.name}}_{{method.name}}_ResponseParams,
+ responseParams);
+ var message = builder.finish();
+ responder.accept(message);
+ });
+{%- endif %}
+{%- endfor %}
+ default:
+ return Promise.reject(Error("Unhandled message: " + reader.messageName));
+ }
+ };
+
+{#--- Validation #}
+
+ function validate{{interface.name}}Request(messageValidator) {
+{%- if not(interface.methods) %}
+ return validator.validationError.NONE;
+{%- else %}
+ var message = messageValidator.message;
+ var paramsClass = null;
+ switch (message.getName()) {
+{%- for method in interface.methods %}
+ case k{{interface.name}}_{{method.name}}_Name:
+{%- if method.response_parameters == None %}
+ if (!message.expectsResponse() && !message.isResponse())
+ paramsClass = {{interface.name}}_{{method.name}}_Params;
+{%- else %}
+ if (message.expectsResponse())
+ paramsClass = {{interface.name}}_{{method.name}}_Params;
+{%- endif %}
+ break;
+{%- endfor %}
+ }
+ if (paramsClass === null)
+ return validator.validationError.NONE;
+ return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+{%- endif %}
+ }
+
+ function validate{{interface.name}}Response(messageValidator) {
+{%- if not(interface|has_callbacks) %}
+ return validator.validationError.NONE;
+{%- else %}
+ var message = messageValidator.message;
+ var paramsClass = null;
+ switch (message.getName()) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters != None %}
+ case k{{interface.name}}_{{method.name}}_Name:
+ if (message.isResponse())
+ paramsClass = {{interface.name}}_{{method.name}}_ResponseParams;
+ break;
+{%- endif %}
+{%- endfor %}
+ }
+ if (paramsClass === null)
+ return validator.validationError.NONE;
+ return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+{%- endif %}
+ }
+
+ {{interface.name}}Stub.prototype.validator = validate{{interface.name}}Request;
+{%- if interface|has_callbacks %}
+ {{interface.name}}Proxy.prototype.validator = validate{{interface.name}}Response;
+{%- else %}
+ {{interface.name}}Proxy.prototype.validator = null;
+{%- endif -%}
+
+{#--- Enums #}
+{% from "enum_definition.tmpl" import enum_def -%}
+{% for enum in interface.enums %}
+ {{enum_def("%sProxy.%s"|format(interface.name, enum.name), enum, module)}}
+ {{interface.name}}Stub.{{enum.name}} = {{interface.name}}Proxy.{{enum.name}};
+{%- endfor %}
+
+{#--- Constants. #}
+{% for constant in interface.constants %}
+ {{interface.name}}Proxy.{{constant.name}} = {{constant.value|expression_to_text}};
+ {{interface.name}}Stub.{{constant.name}} = {{interface.name}}Proxy.{{constant.name}};
+{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl
new file mode 100644
index 0000000..93fa537
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl
@@ -0,0 +1,53 @@
+// 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.
+
+define("{{module.path}}", [
+ "mojo/public/js/bindings/codec",
+ "mojo/public/js/bindings/validator",
+{%- for import in imports %}
+ "{{import.module.path}}",
+{%- endfor %}
+ ], function(codec, validator
+{%- for import in imports -%}
+ , {{import.unique_name}}
+{%- endfor -%}
+) {
+
+{#--- Constants #}
+{% for constant in module.constants %}
+ var {{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+
+{#--- Enums #}
+{%- from "enum_definition.tmpl" import enum_def %}
+{%- for enum in enums %}
+ var {{ enum_def(enum.name, enum, module) }}
+{%- endfor %}
+
+{#--- Struct definitions #}
+{% for struct in structs %}
+{%- include "struct_definition.tmpl" %}
+{%- endfor -%}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces -%}
+{%- include "interface_definition.tmpl" %}
+{%- endfor %}
+
+ var exports = {};
+{%- for constant in module.constants %}
+ exports.{{constant.name}} = {{constant.name}};
+{%- endfor %}
+{%- for enum in enums %}
+ exports.{{enum.name}} = {{enum.name}};
+{%- endfor %}
+{%- for struct in structs if struct.exported %}
+ exports.{{struct.name}} = {{struct.name}};
+{%- endfor %}
+{%- for interface in interfaces %}
+ exports.{{interface.name}}Proxy = {{interface.name}}Proxy;
+ exports.{{interface.name}}Stub = {{interface.name}}Stub;
+{%- endfor %}
+ return exports;
+});
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
new file mode 100644
index 0000000..d77b28b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -0,0 +1,116 @@
+{#--- Begin #}
+ function {{struct.name}}(values) {
+ this.initDefaults_();
+ this.initFields_(values);
+ }
+
+{#--- Enums #}
+{%- from "enum_definition.tmpl" import enum_def %}
+{% for enum in struct.enums %}
+ {{enum_def("%s.%s"|format(struct.name, enum.name), enum, module)}}
+{%- endfor %}
+
+{#--- Constants #}
+{% for constant in struct.constants %}
+ {{struct.name}}.{{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+
+{#--- initDefaults() #}
+ {{struct.name}}.prototype.initDefaults_ = function() {
+{%- for packed_field in struct.packed.packed_fields %}
+ this.{{packed_field.field.name}} = {{packed_field.field|default_value}};
+{%- endfor %}
+ };
+
+{#--- initFields() #}
+ {{struct.name}}.prototype.initFields_ = function(fields) {
+ for(var field in fields) {
+ if (this.hasOwnProperty(field))
+ this[field] = fields[field];
+ }
+ };
+
+{#--- Validation #}
+
+ {{struct.name}}.validate = function(messageValidator, offset) {
+ var err;
+{% macro check_err() -%}
+ if (err !== validator.validationError.NONE)
+ return err;
+{%- endmacro %}
+ err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.packed.packed_fields|length}});
+ {{check_err()}}
+
+{%- for packed_field in struct.packed.packed_fields %}
+{%- set field_name = packed_field.field.name %}
+{%- if packed_field.field|is_string_pointer_field %}
+ // validate {{struct.name}}.{{field_name}}
+ err = messageValidator.validateStringPointer({{packed_field|validate_string_params}})
+ {{check_err()}}
+{%- elif packed_field.field|is_array_pointer_field %}
+ // validate {{struct.name}}.{{field_name}}
+ err = messageValidator.validateArrayPointer({{packed_field|validate_array_params}});
+ {{check_err()}}
+{%- elif packed_field.field|is_struct_pointer_field %}
+ // validate {{struct.name}}.{{field_name}}
+ err = messageValidator.validateStructPointer({{packed_field|validate_struct_params}});
+ {{check_err()}}
+{%- elif packed_field.field|is_handle_field %}
+ // validate {{struct.name}}.{{field_name}}
+ err = messageValidator.validateHandle({{packed_field|validate_handle_params}})
+ {{check_err()}}
+{%- endif %}
+{%- endfor %}
+
+ return validator.validationError.NONE;
+ };
+
+{#--- Encoding and decoding #}
+
+ {{struct.name}}.encodedSize = codec.kStructHeaderSize + {{struct.packed|payload_size}};
+
+ {{struct.name}}.decode = function(decoder) {
+ var packed;
+ var val = new {{struct.name}}();
+ var numberOfBytes = decoder.readUint32();
+ var numberOfFields = decoder.readUint32();
+{%- for byte in struct.bytes %}
+{%- if byte.packed_fields|length > 1 %}
+ packed = decoder.readUint8();
+{%- for packed_field in byte.packed_fields %}
+ val.{{packed_field.field.name}} = (packed >> {{packed_field.bit}}) & 1 ? true : false;
+{%- endfor %}
+{%- else %}
+{%- for packed_field in byte.packed_fields %}
+ val.{{packed_field.field.name}} = decoder.{{packed_field.field.kind|decode_snippet}};
+{%- endfor %}
+{%- endif %}
+{%- if byte.is_padding %}
+ decoder.skip(1);
+{%- endif %}
+{%- endfor %}
+ return val;
+ };
+
+ {{struct.name}}.encode = function(encoder, val) {
+ var packed;
+ encoder.writeUint32({{struct.name}}.encodedSize);
+ encoder.writeUint32({{struct.packed.packed_fields|length}});
+
+{%- for byte in struct.bytes %}
+{%- if byte.packed_fields|length > 1 %}
+ packed = 0;
+{%- for packed_field in byte.packed_fields %}
+ packed |= (val.{{packed_field.field.name}} & 1) << {{packed_field.bit}}
+{%- endfor %}
+ encoder.writeUint8(packed);
+{%- else %}
+{%- for packed_field in byte.packed_fields %}
+ encoder.{{packed_field.field.kind|encode_snippet}}val.{{packed_field.field.name}});
+{%- endfor %}
+{%- endif %}
+{%- if byte.is_padding %}
+ encoder.skip(1);
+{%- endif %}
+{%- endfor %}
+ };
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
new file mode 100644
index 0000000..68e0379
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -0,0 +1,338 @@
+# 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.
+
+"""Generates C++ source files from a mojom.Module."""
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+import mojom.generate.pack as pack
+from mojom.generate.template_expander import UseJinja
+
+
+_kind_to_cpp_type = {
+ mojom.BOOL: "bool",
+ mojom.INT8: "int8_t",
+ mojom.UINT8: "uint8_t",
+ mojom.INT16: "int16_t",
+ mojom.UINT16: "uint16_t",
+ mojom.INT32: "int32_t",
+ mojom.UINT32: "uint32_t",
+ mojom.FLOAT: "float",
+ mojom.HANDLE: "mojo::Handle",
+ mojom.DCPIPE: "mojo::DataPipeConsumerHandle",
+ mojom.DPPIPE: "mojo::DataPipeProducerHandle",
+ mojom.MSGPIPE: "mojo::MessagePipeHandle",
+ mojom.SHAREDBUFFER: "mojo::SharedBufferHandle",
+ mojom.NULLABLE_HANDLE: "mojo::Handle",
+ mojom.NULLABLE_DCPIPE: "mojo::DataPipeConsumerHandle",
+ mojom.NULLABLE_DPPIPE: "mojo::DataPipeProducerHandle",
+ mojom.NULLABLE_MSGPIPE: "mojo::MessagePipeHandle",
+ mojom.NULLABLE_SHAREDBUFFER: "mojo::SharedBufferHandle",
+ mojom.INT64: "int64_t",
+ mojom.UINT64: "uint64_t",
+ mojom.DOUBLE: "double",
+}
+
+_kind_to_cpp_literal_suffix = {
+ mojom.UINT8: "U",
+ mojom.UINT16: "U",
+ mojom.UINT32: "U",
+ mojom.FLOAT: "f",
+ mojom.UINT64: "ULL",
+}
+
+def ConstantValue(constant):
+ return ExpressionToText(constant.value, kind=constant.kind)
+
+def DefaultValue(field):
+ if field.default:
+ if mojom.IsStructKind(field.kind):
+ assert field.default == "default"
+ return "%s::New()" % GetNameForKind(field.kind)
+ return ExpressionToText(field.default, kind=field.kind)
+ return ""
+
+def NamespaceToArray(namespace):
+ return namespace.split('.') if namespace else []
+
+def GetNameForKind(kind, internal = False):
+ parts = []
+ if kind.imported_from:
+ parts.extend(NamespaceToArray(kind.imported_from["namespace"]))
+ if internal:
+ parts.append("internal")
+ if kind.parent_kind:
+ parts.append(kind.parent_kind.name)
+ parts.append(kind.name)
+ return "::".join(parts)
+
+def GetCppType(kind):
+ if mojom.IsStructKind(kind):
+ return "%s_Data*" % GetNameForKind(kind, internal=True)
+ if mojom.IsAnyArrayKind(kind):
+ return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind)
+ if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+ return "mojo::MessagePipeHandle"
+ if mojom.IsEnumKind(kind):
+ return "int32_t"
+ if mojom.IsStringKind(kind):
+ return "mojo::internal::String_Data*"
+ return _kind_to_cpp_type[kind]
+
+def GetCppPodType(kind):
+ if mojom.IsStringKind(kind):
+ return "char*"
+ return _kind_to_cpp_type[kind]
+
+def GetCppArrayArgWrapperType(kind):
+ if mojom.IsEnumKind(kind):
+ return GetNameForKind(kind)
+ if mojom.IsStructKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsAnyArrayKind(kind):
+ return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind)
+ if mojom.IsInterfaceKind(kind):
+ raise Exception("Arrays of interfaces not yet supported!")
+ if mojom.IsInterfaceRequestKind(kind):
+ raise Exception("Arrays of interface requests not yet supported!")
+ if mojom.IsStringKind(kind):
+ return "mojo::String"
+ if mojom.IsHandleKind(kind):
+ return "mojo::ScopedHandle"
+ if mojom.IsDataPipeConsumerKind(kind):
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if mojom.IsDataPipeProducerKind(kind):
+ return "mojo::ScopedDataPipeProducerHandle"
+ if mojom.IsMessagePipeKind(kind):
+ return "mojo::ScopedMessagePipeHandle"
+ if mojom.IsSharedBufferKind(kind):
+ return "mojo::ScopedSharedBufferHandle"
+ return _kind_to_cpp_type[kind]
+
+def GetCppResultWrapperType(kind):
+ if mojom.IsEnumKind(kind):
+ return GetNameForKind(kind)
+ if mojom.IsStructKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsAnyArrayKind(kind):
+ return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+ if mojom.IsInterfaceKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
+ if mojom.IsStringKind(kind):
+ return "mojo::String"
+ if mojom.IsHandleKind(kind):
+ return "mojo::ScopedHandle"
+ if mojom.IsDataPipeConsumerKind(kind):
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if mojom.IsDataPipeProducerKind(kind):
+ return "mojo::ScopedDataPipeProducerHandle"
+ if mojom.IsMessagePipeKind(kind):
+ return "mojo::ScopedMessagePipeHandle"
+ if mojom.IsSharedBufferKind(kind):
+ return "mojo::ScopedSharedBufferHandle"
+ return _kind_to_cpp_type[kind]
+
+def GetCppWrapperType(kind):
+ if mojom.IsEnumKind(kind):
+ return GetNameForKind(kind)
+ if mojom.IsStructKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsAnyArrayKind(kind):
+ return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+ if mojom.IsInterfaceKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ raise Exception("InterfaceRequest fields not supported!")
+ if mojom.IsStringKind(kind):
+ return "mojo::String"
+ if mojom.IsHandleKind(kind):
+ return "mojo::ScopedHandle"
+ if mojom.IsDataPipeConsumerKind(kind):
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if mojom.IsDataPipeProducerKind(kind):
+ return "mojo::ScopedDataPipeProducerHandle"
+ if mojom.IsMessagePipeKind(kind):
+ return "mojo::ScopedMessagePipeHandle"
+ if mojom.IsSharedBufferKind(kind):
+ return "mojo::ScopedSharedBufferHandle"
+ return _kind_to_cpp_type[kind]
+
+def GetCppConstWrapperType(kind):
+ if mojom.IsStructKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsAnyArrayKind(kind):
+ return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+ if mojom.IsInterfaceKind(kind):
+ return "%sPtr" % GetNameForKind(kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
+ if mojom.IsEnumKind(kind):
+ return GetNameForKind(kind)
+ if mojom.IsStringKind(kind):
+ return "const mojo::String&"
+ if mojom.IsHandleKind(kind):
+ return "mojo::ScopedHandle"
+ if mojom.IsDataPipeConsumerKind(kind):
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if mojom.IsDataPipeProducerKind(kind):
+ return "mojo::ScopedDataPipeProducerHandle"
+ if mojom.IsMessagePipeKind(kind):
+ return "mojo::ScopedMessagePipeHandle"
+ if mojom.IsSharedBufferKind(kind):
+ return "mojo::ScopedSharedBufferHandle"
+ if not kind in _kind_to_cpp_type:
+ print "missing:", kind.spec
+ return _kind_to_cpp_type[kind]
+
+def GetCppFieldType(kind):
+ if mojom.IsStructKind(kind):
+ return ("mojo::internal::StructPointer<%s_Data>" %
+ GetNameForKind(kind, internal=True))
+ if mojom.IsAnyArrayKind(kind):
+ return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind)
+ if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+ return "mojo::MessagePipeHandle"
+ if mojom.IsEnumKind(kind):
+ return GetNameForKind(kind)
+ if mojom.IsStringKind(kind):
+ return "mojo::internal::StringPointer"
+ return _kind_to_cpp_type[kind]
+
+def IsStructWithHandles(struct):
+ for pf in struct.packed.packed_fields:
+ if mojom.IsAnyHandleKind(pf.field.kind):
+ return True
+ return False
+
+def TranslateConstants(token, kind):
+ if isinstance(token, mojom.NamedValue):
+ # Both variable and enum constants are constructed like:
+ # Namespace::Struct::CONSTANT_NAME
+ # For enums, CONSTANT_NAME is ENUM_NAME_ENUM_VALUE.
+ name = []
+ if token.imported_from:
+ name.extend(NamespaceToArray(token.namespace))
+ if token.parent_kind:
+ name.append(token.parent_kind.name)
+ if isinstance(token, mojom.EnumValue):
+ name.append(
+ "%s_%s" % (generator.CamelCaseToAllCaps(token.enum.name), token.name))
+ else:
+ name.append(token.name)
+ return "::".join(name)
+
+ if isinstance(token, mojom.BuiltinValue):
+ if token.value == "double.INFINITY" or token.value == "float.INFINITY":
+ return "INFINITY";
+ if token.value == "double.NEGATIVE_INFINITY" or \
+ token.value == "float.NEGATIVE_INFINITY":
+ return "-INFINITY";
+ if token.value == "double.NAN" or token.value == "float.NAN":
+ return "NAN";
+
+ if (kind is not None and mojom.IsFloatKind(kind)):
+ return token if token.isdigit() else token + "f";
+
+ return '%s%s' % (token, _kind_to_cpp_literal_suffix.get(kind, ''))
+
+def ExpressionToText(value, kind=None):
+ return TranslateConstants(value, kind)
+
+def ShouldInlineStruct(struct):
+ # TODO(darin): Base this on the size of the wrapper class.
+ if len(struct.fields) > 4:
+ return False
+ for field in struct.fields:
+ if mojom.IsMoveOnlyKind(field.kind):
+ return False
+ return True
+
+def GetArrayValidateParams(kind):
+ if not mojom.IsAnyArrayKind(kind) and not mojom.IsStringKind(kind):
+ return "mojo::internal::NoValidateParams"
+
+ if mojom.IsStringKind(kind):
+ expected_num_elements = 0
+ element_is_nullable = False
+ element_validate_params = "mojo::internal::NoValidateParams"
+ else:
+ expected_num_elements = generator.ExpectedArraySize(kind)
+ element_is_nullable = mojom.IsNullableKind(kind.kind)
+ element_validate_params = GetArrayValidateParams(kind.kind)
+
+ return "mojo::internal::ArrayValidateParams<%d, %s,\n%s> " % (
+ expected_num_elements,
+ 'true' if element_is_nullable else 'false',
+ element_validate_params)
+
+_HEADER_SIZE = 8
+
+class Generator(generator.Generator):
+
+ cpp_filters = {
+ "constant_value": ConstantValue,
+ "cpp_const_wrapper_type": GetCppConstWrapperType,
+ "cpp_field_type": GetCppFieldType,
+ "cpp_pod_type": GetCppPodType,
+ "cpp_result_type": GetCppResultWrapperType,
+ "cpp_type": GetCppType,
+ "cpp_wrapper_type": GetCppWrapperType,
+ "default_value": DefaultValue,
+ "expected_array_size": generator.ExpectedArraySize,
+ "expression_to_text": ExpressionToText,
+ "get_array_validate_params": GetArrayValidateParams,
+ "get_name_for_kind": GetNameForKind,
+ "get_pad": pack.GetPad,
+ "has_callbacks": mojom.HasCallbacks,
+ "should_inline": ShouldInlineStruct,
+ "is_any_array_kind": mojom.IsAnyArrayKind,
+ "is_cloneable_kind": mojom.IsCloneableKind,
+ "is_enum_kind": mojom.IsEnumKind,
+ "is_move_only_kind": mojom.IsMoveOnlyKind,
+ "is_any_handle_kind": mojom.IsAnyHandleKind,
+ "is_interface_kind": mojom.IsInterfaceKind,
+ "is_interface_request_kind": mojom.IsInterfaceRequestKind,
+ "is_nullable_kind": mojom.IsNullableKind,
+ "is_object_kind": mojom.IsObjectKind,
+ "is_string_kind": mojom.IsStringKind,
+ "is_struct_kind": mojom.IsStructKind,
+ "is_struct_with_handles": IsStructWithHandles,
+ "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
+ "struct_from_method": generator.GetStructFromMethod,
+ "response_struct_from_method": generator.GetResponseStructFromMethod,
+ "stylize_method": generator.StudlyCapsToCamel,
+ "to_all_caps": generator.CamelCaseToAllCaps,
+ }
+
+ def GetJinjaExports(self):
+ return {
+ "module": self.module,
+ "namespace": self.module.namespace,
+ "namespaces_as_array": NamespaceToArray(self.module.namespace),
+ "imports": self.module.imports,
+ "kinds": self.module.kinds,
+ "enums": self.module.enums,
+ "structs": self.GetStructs(),
+ "interfaces": self.module.interfaces,
+ }
+
+ @UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
+ def GenerateModuleHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("cpp_templates/module-internal.h.tmpl", filters=cpp_filters)
+ def GenerateModuleInternalHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters)
+ def GenerateModuleSource(self):
+ return self.GetJinjaExports()
+
+ def GenerateFiles(self, args):
+ self.Write(self.GenerateModuleHeader(), "%s.h" % self.module.name)
+ self.Write(self.GenerateModuleInternalHeader(),
+ "%s-internal.h" % self.module.name)
+ self.Write(self.GenerateModuleSource(), "%s.cc" % self.module.name)
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py
new file mode 100644
index 0000000..043d9e3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -0,0 +1,504 @@
+# 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.
+
+"""Generates java source files from a mojom.Module."""
+
+import argparse
+import ast
+import contextlib
+import os
+import re
+import shutil
+import tempfile
+import zipfile
+
+from jinja2 import contextfilter
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+
+GENERATOR_PREFIX = 'java'
+
+_HEADER_SIZE = 8
+
+_spec_to_java_type = {
+ mojom.BOOL.spec: 'boolean',
+ mojom.DCPIPE.spec: 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
+ mojom.DOUBLE.spec: 'double',
+ mojom.DPPIPE.spec: 'org.chromium.mojo.system.DataPipe.ProducerHandle',
+ mojom.FLOAT.spec: 'float',
+ mojom.HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle',
+ mojom.INT16.spec: 'short',
+ mojom.INT32.spec: 'int',
+ mojom.INT64.spec: 'long',
+ mojom.INT8.spec: 'byte',
+ mojom.MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle',
+ mojom.NULLABLE_DCPIPE.spec:
+ 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
+ mojom.NULLABLE_DPPIPE.spec:
+ 'org.chromium.mojo.system.DataPipe.ProducerHandle',
+ mojom.NULLABLE_HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle',
+ mojom.NULLABLE_MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle',
+ mojom.NULLABLE_SHAREDBUFFER.spec:
+ 'org.chromium.mojo.system.SharedBufferHandle',
+ mojom.NULLABLE_STRING.spec: 'String',
+ mojom.SHAREDBUFFER.spec: 'org.chromium.mojo.system.SharedBufferHandle',
+ mojom.STRING.spec: 'String',
+ mojom.UINT16.spec: 'short',
+ mojom.UINT32.spec: 'int',
+ mojom.UINT64.spec: 'long',
+ mojom.UINT8.spec: 'byte',
+}
+
+_spec_to_decode_method = {
+ mojom.BOOL.spec: 'readBoolean',
+ mojom.DCPIPE.spec: 'readConsumerHandle',
+ mojom.DOUBLE.spec: 'readDouble',
+ mojom.DPPIPE.spec: 'readProducerHandle',
+ mojom.FLOAT.spec: 'readFloat',
+ mojom.HANDLE.spec: 'readUntypedHandle',
+ mojom.INT16.spec: 'readShort',
+ mojom.INT32.spec: 'readInt',
+ mojom.INT64.spec: 'readLong',
+ mojom.INT8.spec: 'readByte',
+ mojom.MSGPIPE.spec: 'readMessagePipeHandle',
+ mojom.NULLABLE_DCPIPE.spec: 'readConsumerHandle',
+ mojom.NULLABLE_DPPIPE.spec: 'readProducerHandle',
+ mojom.NULLABLE_HANDLE.spec: 'readUntypedHandle',
+ mojom.NULLABLE_MSGPIPE.spec: 'readMessagePipeHandle',
+ mojom.NULLABLE_SHAREDBUFFER.spec: 'readSharedBufferHandle',
+ mojom.NULLABLE_STRING.spec: 'readString',
+ mojom.SHAREDBUFFER.spec: 'readSharedBufferHandle',
+ mojom.STRING.spec: 'readString',
+ mojom.UINT16.spec: 'readShort',
+ mojom.UINT32.spec: 'readInt',
+ mojom.UINT64.spec: 'readLong',
+ mojom.UINT8.spec: 'readByte',
+}
+
+_java_primitive_to_boxed_type = {
+ 'boolean': 'Boolean',
+ 'byte': 'Byte',
+ 'double': 'Double',
+ 'float': 'Float',
+ 'int': 'Integer',
+ 'long': 'Long',
+ 'short': 'Short',
+}
+
+
+def NameToComponent(name):
+ # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
+ # HTTP_Entry2_FooBar)
+ name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
+ # insert '_' between non upper and start of upper blocks (e.g.,
+ # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
+ name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
+ return [x.lower() for x in name.split('_')]
+
+def UpperCamelCase(name):
+ return ''.join([x.capitalize() for x in NameToComponent(name)])
+
+def CamelCase(name):
+ uccc = UpperCamelCase(name)
+ return uccc[0].lower() + uccc[1:]
+
+def ConstantStyle(name):
+ components = NameToComponent(name)
+ if components[0] == 'k' and len(components) > 1:
+ components = components[1:]
+ # variable cannot starts with a digit.
+ if components[0][0].isdigit():
+ components[0] = '_' + components[0]
+ return '_'.join([x.upper() for x in components])
+
+def GetNameForElement(element):
+ if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or
+ mojom.IsStructKind(element)):
+ return UpperCamelCase(element.name)
+ if mojom.IsInterfaceRequestKind(element):
+ return GetNameForElement(element.kind)
+ if isinstance(element, (mojom.Method,
+ mojom.Parameter,
+ mojom.Field)):
+ return CamelCase(element.name)
+ if isinstance(element, mojom.EnumValue):
+ return (GetNameForElement(element.enum) + '.' +
+ ConstantStyle(element.name))
+ if isinstance(element, (mojom.NamedValue,
+ mojom.Constant,
+ mojom.EnumField)):
+ return ConstantStyle(element.name)
+ raise Exception('Unexpected element: %s' % element)
+
+def GetInterfaceResponseName(method):
+ return UpperCamelCase(method.name + 'Response')
+
+def ParseStringAttribute(attribute):
+ assert isinstance(attribute, basestring)
+ return attribute
+
+def GetJavaTrueFalse(value):
+ return 'true' if value else 'false'
+
+def GetArrayNullabilityFlags(kind):
+ """Returns nullability flags for an array type, see Decoder.java.
+
+ As we have dedicated decoding functions for arrays, we have to pass
+ nullability information about both the array itself, as well as the array
+ element type there.
+ """
+ assert mojom.IsAnyArrayKind(kind)
+ ARRAY_NULLABLE = \
+ 'org.chromium.mojo.bindings.BindingsHelper.ARRAY_NULLABLE'
+ ELEMENT_NULLABLE = \
+ 'org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE'
+ NOTHING_NULLABLE = \
+ 'org.chromium.mojo.bindings.BindingsHelper.NOTHING_NULLABLE'
+
+ flags_to_set = []
+ if mojom.IsNullableKind(kind):
+ flags_to_set.append(ARRAY_NULLABLE)
+ if mojom.IsNullableKind(kind.kind):
+ flags_to_set.append(ELEMENT_NULLABLE)
+
+ if not flags_to_set:
+ flags_to_set = [NOTHING_NULLABLE]
+ return ' | '.join(flags_to_set)
+
+
+def AppendEncodeDecodeParams(initial_params, context, kind, bit):
+ """ Appends standard parameters shared between encode and decode calls. """
+ params = list(initial_params)
+ if (kind == mojom.BOOL):
+ params.append(str(bit))
+ if mojom.IsReferenceKind(kind):
+ if mojom.IsAnyArrayKind(kind):
+ params.append(GetArrayNullabilityFlags(kind))
+ else:
+ params.append(GetJavaTrueFalse(mojom.IsNullableKind(kind)))
+ if mojom.IsAnyArrayKind(kind):
+ if mojom.IsFixedArrayKind(kind):
+ params.append(str(kind.length))
+ else:
+ params.append(
+ 'org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH');
+ if mojom.IsInterfaceKind(kind):
+ params.append('%s.MANAGER' % GetJavaType(context, kind))
+ if mojom.IsAnyArrayKind(kind) and mojom.IsInterfaceKind(kind.kind):
+ params.append('%s.MANAGER' % GetJavaType(context, kind.kind))
+ return params
+
+
+@contextfilter
+def DecodeMethod(context, kind, offset, bit):
+ def _DecodeMethodName(kind):
+ if mojom.IsAnyArrayKind(kind):
+ return _DecodeMethodName(kind.kind) + 's'
+ if mojom.IsEnumKind(kind):
+ return _DecodeMethodName(mojom.INT32)
+ if mojom.IsInterfaceRequestKind(kind):
+ return 'readInterfaceRequest'
+ if mojom.IsInterfaceKind(kind):
+ return 'readServiceInterface'
+ return _spec_to_decode_method[kind.spec]
+ methodName = _DecodeMethodName(kind)
+ params = AppendEncodeDecodeParams([ str(offset) ], context, kind, bit)
+ return '%s(%s)' % (methodName, ', '.join(params))
+
+@contextfilter
+def EncodeMethod(context, kind, variable, offset, bit):
+ params = AppendEncodeDecodeParams(
+ [ variable, str(offset) ], context, kind, bit)
+ return 'encode(%s)' % ', '.join(params)
+
+def GetPackage(module):
+ if 'JavaPackage' in module.attributes:
+ return ParseStringAttribute(module.attributes['JavaPackage'])
+ # Default package.
+ if module.namespace:
+ return 'org.chromium.mojom.' + module.namespace
+ return 'org.chromium.mojom'
+
+def GetNameForKind(context, kind):
+ def _GetNameHierachy(kind):
+ hierachy = []
+ if kind.parent_kind:
+ hierachy = _GetNameHierachy(kind.parent_kind)
+ hierachy.append(GetNameForElement(kind))
+ return hierachy
+
+ module = context.resolve('module')
+ elements = []
+ if GetPackage(module) != GetPackage(kind.module):
+ elements += [GetPackage(kind.module)]
+ elements += _GetNameHierachy(kind)
+ return '.'.join(elements)
+
+def GetBoxedJavaType(context, kind):
+ unboxed_type = GetJavaType(context, kind, False)
+ if unboxed_type in _java_primitive_to_boxed_type:
+ return _java_primitive_to_boxed_type[unboxed_type]
+ return unboxed_type
+
+@contextfilter
+def GetJavaType(context, kind, boxed=False):
+ if boxed:
+ return GetBoxedJavaType(context, kind)
+ if mojom.IsStructKind(kind) or mojom.IsInterfaceKind(kind):
+ return GetNameForKind(context, kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return ('org.chromium.mojo.bindings.InterfaceRequest<%s>' %
+ GetNameForKind(context, kind.kind))
+ if mojom.IsAnyArrayKind(kind):
+ return '%s[]' % GetJavaType(context, kind.kind)
+ if mojom.IsEnumKind(kind):
+ return 'int'
+ return _spec_to_java_type[kind.spec]
+
+@contextfilter
+def DefaultValue(context, field):
+ assert field.default
+ if isinstance(field.kind, mojom.Struct):
+ assert field.default == 'default'
+ return 'new %s()' % GetJavaType(context, field.kind)
+ return '(%s) %s' % (
+ GetJavaType(context, field.kind),
+ ExpressionToText(context, field.default, kind_spec=field.kind.spec))
+
+@contextfilter
+def ConstantValue(context, constant):
+ return '(%s) %s' % (
+ GetJavaType(context, constant.kind),
+ ExpressionToText(context, constant.value, kind_spec=constant.kind.spec))
+
+@contextfilter
+def NewArray(context, kind, size):
+ if mojom.IsAnyArrayKind(kind.kind):
+ return NewArray(context, kind.kind, size) + '[]'
+ return 'new %s[%s]' % (GetJavaType(context, kind.kind), size)
+
+@contextfilter
+def ExpressionToText(context, token, kind_spec=''):
+ def _TranslateNamedValue(named_value):
+ entity_name = GetNameForElement(named_value)
+ if named_value.parent_kind:
+ return GetJavaType(context, named_value.parent_kind) + '.' + entity_name
+ # Handle the case where named_value is a module level constant:
+ if not isinstance(named_value, mojom.EnumValue):
+ entity_name = (GetConstantsMainEntityName(named_value.module) + '.' +
+ entity_name)
+ if GetPackage(named_value.module) == GetPackage(context.resolve('module')):
+ return entity_name
+ return GetPackage(named_value.module) + '.' + entity_name
+
+ if isinstance(token, mojom.NamedValue):
+ return _TranslateNamedValue(token)
+ if kind_spec.startswith('i') or kind_spec.startswith('u'):
+ # Add Long suffix to all integer literals.
+ number = ast.literal_eval(token.lstrip('+ '))
+ if not isinstance(number, (int, long)):
+ raise ValueError('got unexpected type %r for int literal %r' % (
+ type(number), token))
+ # If the literal is too large to fit a signed long, convert it to the
+ # equivalent signed long.
+ if number >= 2 ** 63:
+ number -= 2 ** 64
+ return '%dL' % number
+ if isinstance(token, mojom.BuiltinValue):
+ if token.value == 'double.INFINITY':
+ return 'java.lang.Double.POSITIVE_INFINITY'
+ if token.value == 'double.NEGATIVE_INFINITY':
+ return 'java.lang.Double.NEGATIVE_INFINITY'
+ if token.value == 'double.NAN':
+ return 'java.lang.Double.NaN'
+ if token.value == 'float.INFINITY':
+ return 'java.lang.Float.POSITIVE_INFINITY'
+ if token.value == 'float.NEGATIVE_INFINITY':
+ return 'java.lang.Float.NEGATIVE_INFINITY'
+ if token.value == 'float.NAN':
+ return 'java.lang.Float.NaN'
+ return token
+
+def IsPointerArrayKind(kind):
+ if not mojom.IsAnyArrayKind(kind):
+ return False
+ sub_kind = kind.kind
+ return mojom.IsObjectKind(sub_kind)
+
+def GetResponseStructFromMethod(method):
+ return generator.GetDataHeader(
+ False, generator.GetResponseStructFromMethod(method))
+
+def GetStructFromMethod(method):
+ return generator.GetDataHeader(
+ False, generator.GetStructFromMethod(method))
+
+def GetConstantsMainEntityName(module):
+ if 'JavaConstantsClassName' in module.attributes:
+ return ParseStringAttribute(module.attributes['JavaConstantsClassName'])
+ # This constructs the name of the embedding classes for module level constants
+ # by extracting the mojom's filename and prepending it to Constants.
+ return (UpperCamelCase(module.path.split('/')[-1].rsplit('.', 1)[0]) +
+ 'Constants')
+
+def GetMethodOrdinalName(method):
+ return ConstantStyle(method.name) + '_ORDINAL'
+
+def HasMethodWithResponse(interface):
+ for method in interface.methods:
+ if method.response_parameters is not None:
+ return True
+ return False
+
+def HasMethodWithoutResponse(interface):
+ for method in interface.methods:
+ if method.response_parameters is None:
+ return True
+ return False
+
+@contextlib.contextmanager
+def TempDir():
+ dirname = tempfile.mkdtemp()
+ try:
+ yield dirname
+ finally:
+ shutil.rmtree(dirname)
+
+def ZipContentInto(root, zip_filename):
+ with zipfile.ZipFile(zip_filename, 'w') as zip_file:
+ for dirname, _, files in os.walk(root):
+ for filename in files:
+ path = os.path.join(dirname, filename)
+ path_in_archive = os.path.relpath(path, root)
+ zip_file.write(path, path_in_archive)
+
+class Generator(generator.Generator):
+
+ java_filters = {
+ 'interface_response_name': GetInterfaceResponseName,
+ 'constant_value': ConstantValue,
+ 'default_value': DefaultValue,
+ 'decode_method': DecodeMethod,
+ 'expression_to_text': ExpressionToText,
+ 'encode_method': EncodeMethod,
+ 'has_method_with_response': HasMethodWithResponse,
+ 'has_method_without_response': HasMethodWithoutResponse,
+ 'is_fixed_array_kind': mojom.IsFixedArrayKind,
+ 'is_handle': mojom.IsNonInterfaceHandleKind,
+ 'is_nullable_kind': mojom.IsNullableKind,
+ 'is_pointer_array_kind': IsPointerArrayKind,
+ 'is_struct_kind': mojom.IsStructKind,
+ 'java_type': GetJavaType,
+ 'java_true_false': GetJavaTrueFalse,
+ 'method_ordinal_name': GetMethodOrdinalName,
+ 'name': GetNameForElement,
+ 'new_array': NewArray,
+ 'response_struct_from_method': GetResponseStructFromMethod,
+ 'struct_from_method': GetStructFromMethod,
+ 'struct_size': lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
+ }
+
+ def GetJinjaExports(self):
+ return {
+ 'package': GetPackage(self.module),
+ }
+
+ def GetJinjaExportsForInterface(self, interface):
+ exports = self.GetJinjaExports()
+ exports.update({'interface': interface})
+ if interface.client:
+ for client in self.module.interfaces:
+ if client.name == interface.client:
+ exports.update({'client': client})
+ return exports
+
+ @UseJinja('java_templates/enum.java.tmpl', filters=java_filters)
+ def GenerateEnumSource(self, enum):
+ exports = self.GetJinjaExports()
+ exports.update({'enum': enum})
+ return exports
+
+ @UseJinja('java_templates/struct.java.tmpl', filters=java_filters)
+ def GenerateStructSource(self, struct):
+ exports = self.GetJinjaExports()
+ exports.update({'struct': struct})
+ return exports
+
+ @UseJinja('java_templates/interface.java.tmpl', filters=java_filters)
+ def GenerateInterfaceSource(self, interface):
+ return self.GetJinjaExportsForInterface(interface)
+
+ @UseJinja('java_templates/interface_internal.java.tmpl', filters=java_filters)
+ def GenerateInterfaceInternalSource(self, interface):
+ return self.GetJinjaExportsForInterface(interface)
+
+ @UseJinja('java_templates/constants.java.tmpl', filters=java_filters)
+ def GenerateConstantsSource(self, module):
+ exports = self.GetJinjaExports()
+ exports.update({'main_entity': GetConstantsMainEntityName(module),
+ 'constants': module.constants})
+ return exports
+
+ def DoGenerateFiles(self):
+ if not os.path.exists(self.output_dir):
+ try:
+ os.makedirs(self.output_dir)
+ except:
+ # Ignore errors on directory creation.
+ pass
+
+ # Keep this above the others as .GetStructs() changes the state of the
+ # module, annotating structs with required information.
+ for struct in self.GetStructs():
+ self.Write(self.GenerateStructSource(struct),
+ '%s.java' % GetNameForElement(struct))
+
+ for enum in self.module.enums:
+ self.Write(self.GenerateEnumSource(enum),
+ '%s.java' % GetNameForElement(enum))
+
+ for interface in self.module.interfaces:
+ self.Write(self.GenerateInterfaceSource(interface),
+ '%s.java' % GetNameForElement(interface))
+ self.Write(self.GenerateInterfaceInternalSource(interface),
+ '%s_Internal.java' % GetNameForElement(interface))
+
+ if self.module.constants:
+ self.Write(self.GenerateConstantsSource(self.module),
+ '%s.java' % GetConstantsMainEntityName(self.module))
+
+ def GenerateFiles(self, unparsed_args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--java_output_directory', dest='java_output_directory')
+ args = parser.parse_args(unparsed_args)
+ package_path = GetPackage(self.module).replace('.', '/')
+
+ # Generate the java files in a temporary directory and place a single
+ # srcjar in the output directory.
+ zip_filename = os.path.join(self.output_dir,
+ "%s.srcjar" % self.module.name)
+ with TempDir() as temp_java_root:
+ self.output_dir = os.path.join(temp_java_root, package_path)
+ self.DoGenerateFiles();
+ ZipContentInto(temp_java_root, zip_filename)
+
+ if args.java_output_directory:
+ # If requested, generate the java files directly into indicated directory.
+ self.output_dir = os.path.join(args.java_output_directory, package_path)
+ self.DoGenerateFiles();
+
+ def GetJinjaParameters(self):
+ return {
+ 'lstrip_blocks': True,
+ 'trim_blocks': True,
+ }
+
+ def GetGlobals(self):
+ return {
+ 'namespace': self.module.namespace,
+ 'module': self.module,
+ }
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
new file mode 100644
index 0000000..c9109fb
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -0,0 +1,275 @@
+# 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.
+
+"""Generates JavaScript source files from a mojom.Module."""
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+import mojom.generate.pack as pack
+from mojom.generate.template_expander import UseJinja
+
+_kind_to_javascript_default_value = {
+ mojom.BOOL: "false",
+ mojom.INT8: "0",
+ mojom.UINT8: "0",
+ mojom.INT16: "0",
+ mojom.UINT16: "0",
+ mojom.INT32: "0",
+ mojom.UINT32: "0",
+ mojom.FLOAT: "0",
+ mojom.HANDLE: "null",
+ mojom.DCPIPE: "null",
+ mojom.DPPIPE: "null",
+ mojom.MSGPIPE: "null",
+ mojom.SHAREDBUFFER: "null",
+ mojom.NULLABLE_HANDLE: "null",
+ mojom.NULLABLE_DCPIPE: "null",
+ mojom.NULLABLE_DPPIPE: "null",
+ mojom.NULLABLE_MSGPIPE: "null",
+ mojom.NULLABLE_SHAREDBUFFER: "null",
+ mojom.INT64: "0",
+ mojom.UINT64: "0",
+ mojom.DOUBLE: "0",
+ mojom.STRING: "null",
+ mojom.NULLABLE_STRING: "null"
+}
+
+
+def JavaScriptType(kind):
+ if kind.imported_from:
+ return kind.imported_from["unique_name"] + "." + kind.name
+ return kind.name
+
+
+def JavaScriptDefaultValue(field):
+ if field.default:
+ if mojom.IsStructKind(field.kind):
+ assert field.default == "default"
+ return "new %s()" % JavaScriptType(field.kind)
+ return ExpressionToText(field.default)
+ if field.kind in mojom.PRIMITIVES:
+ return _kind_to_javascript_default_value[field.kind]
+ if mojom.IsStructKind(field.kind):
+ return "null"
+ if mojom.IsAnyArrayKind(field.kind):
+ return "null"
+ if mojom.IsInterfaceKind(field.kind) or \
+ mojom.IsInterfaceRequestKind(field.kind):
+ return _kind_to_javascript_default_value[mojom.MSGPIPE]
+ if mojom.IsEnumKind(field.kind):
+ return "0"
+
+
+def JavaScriptPayloadSize(packed):
+ packed_fields = packed.packed_fields
+ if not packed_fields:
+ return 0
+ last_field = packed_fields[-1]
+ offset = last_field.offset + last_field.size
+ pad = pack.GetPad(offset, 8)
+ return offset + pad
+
+
+_kind_to_codec_type = {
+ mojom.BOOL: "codec.Uint8",
+ mojom.INT8: "codec.Int8",
+ mojom.UINT8: "codec.Uint8",
+ mojom.INT16: "codec.Int16",
+ mojom.UINT16: "codec.Uint16",
+ mojom.INT32: "codec.Int32",
+ mojom.UINT32: "codec.Uint32",
+ mojom.FLOAT: "codec.Float",
+ mojom.HANDLE: "codec.Handle",
+ mojom.DCPIPE: "codec.Handle",
+ mojom.DPPIPE: "codec.Handle",
+ mojom.MSGPIPE: "codec.Handle",
+ mojom.SHAREDBUFFER: "codec.Handle",
+ mojom.NULLABLE_HANDLE: "codec.NullableHandle",
+ mojom.NULLABLE_DCPIPE: "codec.NullableHandle",
+ mojom.NULLABLE_DPPIPE: "codec.NullableHandle",
+ mojom.NULLABLE_MSGPIPE: "codec.NullableHandle",
+ mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle",
+ mojom.INT64: "codec.Int64",
+ mojom.UINT64: "codec.Uint64",
+ mojom.DOUBLE: "codec.Double",
+ mojom.STRING: "codec.String",
+ mojom.NULLABLE_STRING: "codec.NullableString",
+}
+
+
+def CodecType(kind):
+ if kind in mojom.PRIMITIVES:
+ return _kind_to_codec_type[kind]
+ if mojom.IsStructKind(kind):
+ pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \
+ else "PointerTo"
+ return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind))
+ if mojom.IsAnyArrayKind(kind):
+ array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
+ element_type = "codec.PackedBool" if mojom.IsBoolKind(kind.kind) \
+ else CodecType(kind.kind)
+ return "new codec.%s(%s)" % (array_type, element_type)
+ if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+ return CodecType(mojom.MSGPIPE)
+ if mojom.IsEnumKind(kind):
+ return _kind_to_codec_type[mojom.INT32]
+ return kind
+
+
+def JavaScriptDecodeSnippet(kind):
+ if kind in mojom.PRIMITIVES:
+ return "decodeStruct(%s)" % CodecType(kind)
+ if mojom.IsStructKind(kind):
+ return "decodeStructPointer(%s)" % JavaScriptType(kind)
+ if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+ return "decodeArrayPointer(codec.PackedBool)"
+ if mojom.IsAnyArrayKind(kind):
+ return "decodeArrayPointer(%s)" % CodecType(kind.kind)
+ if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+ return JavaScriptDecodeSnippet(mojom.MSGPIPE)
+ if mojom.IsEnumKind(kind):
+ return JavaScriptDecodeSnippet(mojom.INT32)
+
+
+def JavaScriptEncodeSnippet(kind):
+ if kind in mojom.PRIMITIVES:
+ return "encodeStruct(%s, " % CodecType(kind)
+ if mojom.IsStructKind(kind):
+ return "encodeStructPointer(%s, " % JavaScriptType(kind)
+ if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+ return "encodeArrayPointer(codec.PackedBool, ";
+ if mojom.IsAnyArrayKind(kind):
+ return "encodeArrayPointer(%s, " % CodecType(kind.kind)
+ if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
+ return JavaScriptEncodeSnippet(mojom.MSGPIPE)
+ if mojom.IsEnumKind(kind):
+ return JavaScriptEncodeSnippet(mojom.INT32)
+
+
+def JavaScriptFieldOffset(packed_field):
+ return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
+
+
+def JavaScriptNullableParam(packed_field):
+ return "true" if mojom.IsNullableKind(packed_field.field.kind) else "false"
+
+
+def JavaScriptValidateArrayParams(packed_field):
+ nullable = JavaScriptNullableParam(packed_field)
+ field_offset = JavaScriptFieldOffset(packed_field)
+ element_kind = packed_field.field.kind.kind
+ element_size = pack.PackedField.GetSizeForKind(element_kind)
+ element_count = generator.ExpectedArraySize(packed_field.field.kind)
+ element_type = "codec.PackedBool" if mojom.IsBoolKind(element_kind) \
+ else CodecType(element_kind)
+ return "%s, %s, %s, %s, %s" % \
+ (field_offset, element_size, element_count, element_type, nullable)
+
+
+def JavaScriptValidateStructParams(packed_field):
+ nullable = JavaScriptNullableParam(packed_field)
+ field_offset = JavaScriptFieldOffset(packed_field)
+ struct_type = JavaScriptType(packed_field.field.kind)
+ return "%s, %s, %s" % (field_offset, struct_type, nullable)
+
+
+def JavaScriptValidateStringParams(packed_field):
+ nullable = JavaScriptNullableParam(packed_field)
+ return "%s, %s" % (JavaScriptFieldOffset(packed_field), nullable)
+
+
+def JavaScriptValidateHandleParams(packed_field):
+ nullable = JavaScriptNullableParam(packed_field)
+ field_offset = JavaScriptFieldOffset(packed_field)
+ return "%s, %s" % (field_offset, nullable)
+
+
+def TranslateConstants(token):
+ if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
+ # Both variable and enum constants are constructed like:
+ # NamespaceUid.Struct[.Enum].CONSTANT_NAME
+ name = []
+ if token.imported_from:
+ name.append(token.imported_from["unique_name"])
+ if token.parent_kind:
+ name.append(token.parent_kind.name)
+ if isinstance(token, mojom.EnumValue):
+ name.append(token.enum.name)
+ name.append(token.name)
+ return ".".join(name)
+
+ if isinstance(token, mojom.BuiltinValue):
+ if token.value == "double.INFINITY" or token.value == "float.INFINITY":
+ return "Infinity";
+ if token.value == "double.NEGATIVE_INFINITY" or \
+ token.value == "float.NEGATIVE_INFINITY":
+ return "-Infinity";
+ if token.value == "double.NAN" or token.value == "float.NAN":
+ return "NaN";
+
+ return token
+
+
+def ExpressionToText(value):
+ return TranslateConstants(value)
+
+
+def IsArrayPointerField(field):
+ return mojom.IsAnyArrayKind(field.kind)
+
+def IsStringPointerField(field):
+ return mojom.IsStringKind(field.kind)
+
+def IsStructPointerField(field):
+ return mojom.IsStructKind(field.kind)
+
+def IsHandleField(field):
+ return mojom.IsAnyHandleKind(field.kind)
+
+
+class Generator(generator.Generator):
+
+ js_filters = {
+ "default_value": JavaScriptDefaultValue,
+ "payload_size": JavaScriptPayloadSize,
+ "decode_snippet": JavaScriptDecodeSnippet,
+ "encode_snippet": JavaScriptEncodeSnippet,
+ "expression_to_text": ExpressionToText,
+ "field_offset": JavaScriptFieldOffset,
+ "has_callbacks": mojom.HasCallbacks,
+ "is_array_pointer_field": IsArrayPointerField,
+ "is_struct_pointer_field": IsStructPointerField,
+ "is_string_pointer_field": IsStringPointerField,
+ "is_handle_field": IsHandleField,
+ "js_type": JavaScriptType,
+ "stylize_method": generator.StudlyCapsToCamel,
+ "validate_array_params": JavaScriptValidateArrayParams,
+ "validate_handle_params": JavaScriptValidateHandleParams,
+ "validate_string_params": JavaScriptValidateStringParams,
+ "validate_struct_params": JavaScriptValidateStructParams,
+ }
+
+ @UseJinja("js_templates/module.js.tmpl", filters=js_filters)
+ def GenerateJsModule(self):
+ return {
+ "namespace": self.module.namespace,
+ "imports": self.GetImports(),
+ "kinds": self.module.kinds,
+ "enums": self.module.enums,
+ "module": self.module,
+ "structs": self.GetStructs() + self.GetStructsFromMethods(),
+ "interfaces": self.module.interfaces,
+ }
+
+ def GenerateFiles(self, args):
+ self.Write(self.GenerateJsModule(), "%s.js" % self.module.name)
+
+ def GetImports(self):
+ # Since each import is assigned a variable in JS, they need to have unique
+ # names.
+ counter = 1
+ for each in self.module.imports:
+ each["unique_name"] = "import" + str(counter)
+ counter += 1
+ return self.module.imports
diff --git a/mojo/public/tools/bindings/generators/mojom_python_generator.py b/mojo/public/tools/bindings/generators/mojom_python_generator.py
new file mode 100644
index 0000000..8034480
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_python_generator.py
@@ -0,0 +1,291 @@
+# 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.
+
+"""Generates Python source files from a mojom.Module."""
+
+import re
+from itertools import ifilter
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+_kind_to_type = {
+ mojom.BOOL: '_descriptor.TYPE_BOOL',
+ mojom.INT8: '_descriptor.TYPE_INT8',
+ mojom.UINT8: '_descriptor.TYPE_UINT8',
+ mojom.INT16: '_descriptor.TYPE_INT16',
+ mojom.UINT16: '_descriptor.TYPE_UINT16',
+ mojom.INT32: '_descriptor.TYPE_INT32',
+ mojom.UINT32: '_descriptor.TYPE_UINT32',
+ mojom.INT64: '_descriptor.TYPE_INT64',
+ mojom.UINT64: '_descriptor.TYPE_UINT64',
+ mojom.FLOAT: '_descriptor.TYPE_FLOAT',
+ mojom.DOUBLE: '_descriptor.TYPE_DOUBLE',
+ mojom.STRING: '_descriptor.TYPE_STRING',
+ mojom.NULLABLE_STRING: '_descriptor.TYPE_NULLABLE_STRING',
+ mojom.HANDLE: '_descriptor.TYPE_HANDLE',
+ mojom.DCPIPE: '_descriptor.TYPE_HANDLE',
+ mojom.DPPIPE: '_descriptor.TYPE_HANDLE',
+ mojom.MSGPIPE: '_descriptor.TYPE_HANDLE',
+ mojom.SHAREDBUFFER: '_descriptor.TYPE_HANDLE',
+ mojom.NULLABLE_HANDLE: '_descriptor.TYPE_NULLABLE_HANDLE',
+ mojom.NULLABLE_DCPIPE: '_descriptor.TYPE_NULLABLE_HANDLE',
+ mojom.NULLABLE_DPPIPE: '_descriptor.TYPE_NULLABLE_HANDLE',
+ mojom.NULLABLE_MSGPIPE: '_descriptor.TYPE_NULLABLE_HANDLE',
+ mojom.NULLABLE_SHAREDBUFFER: '_descriptor.TYPE_NULLABLE_HANDLE',
+}
+
+# int64 integers are not handled by array.array. int64/uint64 array are
+# supported but storage is not optimized (ie. they are plain python list, not
+# array.array)
+_kind_to_typecode_for_native_array = {
+ mojom.INT8: 'b',
+ mojom.UINT8: 'B',
+ mojom.INT16: 'h',
+ mojom.UINT16: 'H',
+ mojom.INT32: 'i',
+ mojom.UINT32: 'I',
+ mojom.FLOAT: 'f',
+ mojom.DOUBLE: 'd',
+}
+
+_kind_to_typecode = dict(_kind_to_typecode_for_native_array)
+_kind_to_typecode.update({
+ mojom.INT64: 'q',
+ mojom.UINT64: 'Q',
+ mojom.HANDLE: 'i',
+ mojom.DCPIPE: 'i',
+ mojom.DPPIPE: 'i',
+ mojom.MSGPIPE: 'i',
+ mojom.SHAREDBUFFER: 'i',
+ mojom.NULLABLE_HANDLE: 'i',
+ mojom.NULLABLE_DCPIPE: 'i',
+ mojom.NULLABLE_DPPIPE: 'i',
+ mojom.NULLABLE_MSGPIPE: 'i',
+ mojom.NULLABLE_SHAREDBUFFER: 'i',
+})
+
+
+def NameToComponent(name):
+ # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
+ # HTTP_Entry2_FooBar)
+ name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
+ # insert '_' between non upper and start of upper blocks (e.g.,
+ # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
+ name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
+ return [x.lower() for x in name.split('_')]
+
+def UpperCamelCase(name):
+ return ''.join([x.capitalize() for x in NameToComponent(name)])
+
+def CamelCase(name):
+ uccc = UpperCamelCase(name)
+ return uccc[0].lower() + uccc[1:]
+
+def ConstantStyle(name):
+ components = NameToComponent(name)
+ if components[0] == 'k':
+ components = components[1:]
+ return '_'.join([x.upper() for x in components])
+
+def GetNameForElement(element):
+ if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or
+ mojom.IsStructKind(element)):
+ return UpperCamelCase(element.name)
+ if isinstance(element, mojom.EnumValue):
+ return (GetNameForElement(element.enum) + '.' +
+ ConstantStyle(element.name))
+ if isinstance(element, (mojom.NamedValue,
+ mojom.Constant)):
+ return ConstantStyle(element.name)
+ raise Exception('Unexpected element: ' % element)
+
+def ExpressionToText(token):
+ if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
+ return str(token.computed_value)
+
+ if isinstance(token, mojom.BuiltinValue):
+ if token.value == 'double.INFINITY' or token.value == 'float.INFINITY':
+ return 'float(\'inf\')';
+ if (token.value == 'double.NEGATIVE_INFINITY' or
+ token.value == 'float.NEGATIVE_INFINITY'):
+ return 'float(\'-inf\')'
+ if token.value == 'double.NAN' or token.value == 'float.NAN':
+ return 'float(\'nan\')';
+
+ if token in ['true', 'false']:
+ return str(token == 'true')
+
+ return token
+
+def GetStructClass(kind):
+ name = []
+ if kind.imported_from:
+ name.append(kind.imported_from['python_module'])
+ name.append(GetNameForElement(kind))
+ return '.'.join(name)
+
+def GetFieldType(kind, field=None):
+ if mojom.IsAnyArrayKind(kind):
+ arguments = []
+ if kind.kind in _kind_to_typecode_for_native_array:
+ arguments.append('%r' %_kind_to_typecode_for_native_array[kind.kind])
+ elif kind.kind != mojom.BOOL:
+ arguments.append(GetFieldType(kind.kind))
+ if mojom.IsNullableKind(kind):
+ arguments.append('nullable=True')
+ if mojom.IsFixedArrayKind(kind):
+ arguments.append('length=%d' % kind.length)
+ array_type = 'GenericArrayType'
+ if kind.kind == mojom.BOOL:
+ array_type = 'BooleanArrayType'
+ elif kind.kind in _kind_to_typecode_for_native_array:
+ array_type = 'NativeArrayType'
+ return '_descriptor.%s(%s)' % (array_type, ', '.join(arguments))
+
+ if mojom.IsStructKind(kind):
+ arguments = [ GetStructClass(kind) ]
+ if mojom.IsNullableKind(kind):
+ arguments.append('nullable=True')
+ return '_descriptor.StructType(%s)' % ', '.join(arguments)
+
+ if mojom.IsEnumKind(kind):
+ return GetFieldType(mojom.INT32)
+
+ return _kind_to_type.get(kind, '_descriptor.TYPE_NONE')
+
+def GetFieldDescriptor(packed_field):
+ field = packed_field.field
+ class_name = 'SingleFieldGroup'
+ if field.kind == mojom.BOOL:
+ class_name = 'FieldDescriptor'
+ arguments = [ '%r' % field.name ]
+ arguments.append(GetFieldType(field.kind, field))
+ arguments.append(str(packed_field.index))
+ arguments.append(str(packed_field.ordinal))
+ if field.default:
+ if mojom.IsStructKind(field.kind):
+ arguments.append('default_value=True')
+ else:
+ arguments.append('default_value=%s' % ExpressionToText(field.default))
+ return '_descriptor.%s(%s)' % (class_name, ', '.join(arguments))
+
+def GetFieldGroup(byte):
+ if len(byte.packed_fields) > 1:
+ descriptors = map(GetFieldDescriptor, byte.packed_fields)
+ return '_descriptor.BooleanGroup([%s])' % ', '.join(descriptors)
+ assert len(byte.packed_fields) == 1
+ return GetFieldDescriptor(byte.packed_fields[0])
+
+def ComputeStaticValues(module):
+ in_progress = set()
+ computed = set()
+
+ def GetComputedValue(named_value):
+ if isinstance(named_value, mojom.EnumValue):
+ field = next(ifilter(lambda field: field.name == named_value.name,
+ named_value.enum.fields), None)
+ if not field:
+ raise RuntimeError(
+ 'Unable to get computed value for field %s of enum %s' %
+ (named_value.name, named_value.enum.name))
+ if field not in computed:
+ ResolveEnum(named_value.enum)
+ return field.computed_value
+ elif isinstance(named_value, mojom.ConstantValue):
+ ResolveConstant(named_value.constant)
+ named_value.computed_value = named_value.constant.computed_value
+ return named_value.computed_value
+ else:
+ print named_value
+
+ def ResolveConstant(constant):
+ if constant in computed:
+ return
+ if constant in in_progress:
+ raise RuntimeError('Circular dependency for constant: %s' % constant.name)
+ in_progress.add(constant)
+ if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)):
+ computed_value = GetComputedValue(constant.value)
+ else:
+ computed_value = ExpressionToText(constant.value)
+ constant.computed_value = computed_value
+ in_progress.remove(constant)
+ computed.add(constant)
+
+ def ResolveEnum(enum):
+ def ResolveEnumField(enum, field, default_value):
+ if field in computed:
+ return
+ if field in in_progress:
+ raise RuntimeError('Circular dependency for enum: %s' % enum.name)
+ in_progress.add(field)
+ if field.value:
+ if isinstance(field.value, mojom.EnumValue):
+ computed_value = GetComputedValue(field.value)
+ elif isinstance(field.value, str):
+ computed_value = int(field.value, 0)
+ else:
+ raise RuntimeError('Unexpected value: %s' % field.value)
+ else:
+ computed_value = default_value
+ field.computed_value = computed_value
+ in_progress.remove(field)
+ computed.add(field)
+
+ current_value = 0
+ for field in enum.fields:
+ ResolveEnumField(enum, field, current_value)
+ current_value = field.computed_value + 1
+
+ for constant in module.constants:
+ ResolveConstant(constant)
+
+ for enum in module.enums:
+ ResolveEnum(enum)
+
+ for struct in module.structs:
+ for constant in struct.constants:
+ ResolveConstant(constant)
+ for enum in struct.enums:
+ ResolveEnum(enum)
+ for field in struct.fields:
+ if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)):
+ field.default.computed_value = GetComputedValue(field.default)
+
+ return module
+
+
+class Generator(generator.Generator):
+
+ python_filters = {
+ 'expression_to_text': ExpressionToText,
+ 'field_group': GetFieldGroup,
+ 'name': GetNameForElement,
+ }
+
+ @UseJinja('python_templates/module.py.tmpl', filters=python_filters)
+ def GeneratePythonModule(self):
+ return {
+ 'imports': self.GetImports(),
+ 'enums': self.module.enums,
+ 'module': ComputeStaticValues(self.module),
+ 'structs': self.GetStructs(),
+ }
+
+ def GenerateFiles(self, args):
+ self.Write(self.GeneratePythonModule(),
+ '%s.py' % self.module.name.replace('.mojom', '_mojom'))
+
+ def GetImports(self):
+ for each in self.module.imports:
+ each['python_module'] = each['module_name'].replace('.mojom', '_mojom')
+ return self.module.imports
+
+ def GetJinjaParameters(self):
+ return {
+ 'lstrip_blocks': True,
+ 'trim_blocks': True,
+ }
diff --git a/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl b/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
new file mode 100644
index 0000000..d5bff30
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl
@@ -0,0 +1,56 @@
+{% from "module_macros.tmpl" import enum_values %}
+# 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.bindings.descriptor as _descriptor
+import mojo.bindings.reflection as _reflection
+{% if imports %}
+
+{% for import in imports %}
+import {{import.python_module}}
+{% endfor %}
+{% endif %}
+{#--- Constants #}
+{% if module.constants %}
+
+{% for constant in module.constants %}
+{{constant|name}} = {{constant.value|expression_to_text}}
+{% endfor %}
+{% endif %}
+{% for enum in module.enums %}
+
+class {{enum|name}}(object):
+ __metaclass__ = _reflection.MojoEnumType
+ VALUES = {{enum_values(enum)|indent(2)}}
+{% endfor %}
+{% for struct in module.structs %}
+
+class {{struct|name}}(object):
+ __metaclass__ = _reflection.MojoStructType
+ DESCRIPTOR = {
+{% if struct.constants %}
+ 'constants': {
+{% for constant in struct.constants %}
+ '{{constant|name}}': {{constant.value|expression_to_text}},
+{% endfor %}
+ },
+{% endif %}
+{% if struct.enums %}
+ 'enums': {
+{% for enum in struct.enums %}
+ '{{enum|name}}': {{enum_values(enum)|indent(6)}},
+{% endfor %}
+ },
+{% endif %}
+{% if struct.fields %}
+ 'fields': [
+{% for byte in struct.bytes %}
+{% if byte.packed_fields %}
+ {{byte|field_group}},
+{% endif %}
+{% endfor %}
+ ],
+{% endif %}
+ }
+{% endfor %}
diff --git a/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl b/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl
new file mode 100644
index 0000000..305b26a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl
@@ -0,0 +1,11 @@
+# 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.
+
+{%- macro enum_values(enum) -%}
+[
+{% for field in enum.fields %}
+ ('{{field.name}}', {{field.computed_value}}),
+{% endfor %}
+]
+{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/run_cpp_generator.py b/mojo/public/tools/bindings/generators/run_cpp_generator.py
new file mode 100755
index 0000000..4c6f597
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/run_cpp_generator.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# 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.
+
+import ast
+import os
+import sys
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(script_dir, os.pardir, "pylib"))
+
+from mojom.generate.data
+import mojom_cpp_generator
+
+def ReadDict(file):
+ with open(file, 'r') as f:
+ s = f.read()
+ dict = ast.literal_eval(s)
+ return dict
+
+dict = ReadDict(sys.argv[1])
+module = mojom.generate.data.ModuleFromData(dict)
+dir = None
+if len(sys.argv) > 2:
+ dir = sys.argv[2]
+cpp = mojom_cpp_generator.Generator(module, ".", dir)
+cpp.GenerateFiles([])
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
new file mode 100644
index 0000000..e12d8e0
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -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.
+
+# Generate C++ and JavaScript source files from mojom files. The output files
+# will go under the generated file directory tree with the same path as each
+# input file.
+#
+# Parameters:
+#
+# sources (required)
+# List of source .mojom files to compile.
+#
+# deps (optional)
+#
+# public_deps (optional)
+#
+# testonly (optional)
+#
+# visibility (optional)
+template("mojom") {
+ assert(defined(invoker.sources),
+ "\"sources\" must be defined for the $target_name template.")
+
+ generator_root = "//mojo/public/tools/bindings"
+ generator_script = "$generator_root/mojom_bindings_generator.py"
+ generator_sources = [
+ generator_script,
+ "$generator_root/generators/cpp_templates/enum_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_definition.tmpl",
+ "$generator_root/generators/cpp_templates/interface_macros.tmpl",
+ "$generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/module.cc.tmpl",
+ "$generator_root/generators/cpp_templates/module.h.tmpl",
+ "$generator_root/generators/cpp_templates/module-internal.h.tmpl",
+ "$generator_root/generators/cpp_templates/params_definition.tmpl",
+ "$generator_root/generators/cpp_templates/struct_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/struct_definition.tmpl",
+ "$generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/struct_serialization_definition.tmpl",
+ "$generator_root/generators/cpp_templates/struct_macros.tmpl",
+ "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
+ "$generator_root/generators/js_templates/enum_definition.tmpl",
+ "$generator_root/generators/js_templates/interface_definition.tmpl",
+ "$generator_root/generators/js_templates/module.js.tmpl",
+ "$generator_root/generators/js_templates/struct_definition.tmpl",
+ "$generator_root/generators/python_templates/module_macros.tmpl",
+ "$generator_root/generators/python_templates/module.py.tmpl",
+ "$generator_root/generators/mojom_cpp_generator.py",
+ "$generator_root/generators/mojom_js_generator.py",
+ "$generator_root/generators/mojom_python_generator.py",
+ "$generator_root/pylib/mojom/__init__.py",
+ "$generator_root/pylib/mojom/error.py",
+ "$generator_root/pylib/mojom/generate/__init__.py",
+ "$generator_root/pylib/mojom/generate/data.py",
+ "$generator_root/pylib/mojom/generate/generator.py",
+ "$generator_root/pylib/mojom/generate/module.py",
+ "$generator_root/pylib/mojom/generate/pack.py",
+ "$generator_root/pylib/mojom/generate/template_expander.py",
+ "$generator_root/pylib/mojom/parse/__init__.py",
+ "$generator_root/pylib/mojom/parse/ast.py",
+ "$generator_root/pylib/mojom/parse/lexer.py",
+ "$generator_root/pylib/mojom/parse/parser.py",
+ "$generator_root/pylib/mojom/parse/translate.py",
+ ]
+ generator_cpp_outputs = [
+ "{{source_gen_dir}}/{{source_name_part}}.mojom.cc",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom.h",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-internal.h",
+ ]
+ generator_js_outputs = [
+ "{{source_gen_dir}}/{{source_name_part}}.mojom.js",
+ ]
+ generator_python_outputs = [
+ "{{source_gen_dir}}/{{source_name_part}}_mojom.py",
+ ]
+
+ if (defined(invoker.visibility)) {
+ # Need to save this because the the target_name is overwritten inside the
+ # action to be that of the action itself. Only define this in the case the
+ # var is used to avoid unused var error.
+ target_visibility = [ ":$target_name" ]
+ }
+
+ generator_target_name = target_name + "__generator"
+ action_foreach(generator_target_name) {
+ if (defined(invoker.visibility)) {
+ visibility = target_visibility + invoker.visibility
+ }
+ script = generator_script
+ inputs = generator_sources
+ sources = invoker.sources
+ outputs = generator_cpp_outputs +
+ generator_js_outputs +
+ generator_python_outputs
+ args = [
+ "{{source}}",
+ "--use_chromium_bundled_pylibs",
+ "-d", rebase_path("//", root_build_dir),
+ "-I", rebase_path("//", root_build_dir),
+ "-o", "{{source_gen_dir}}",
+ ]
+ }
+
+ source_set(target_name) {
+ if (defined(invoker.visibility)) {
+ visibility = invoker.visibility
+ }
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ sources = process_file_template(invoker.sources, generator_cpp_outputs)
+ data = process_file_template(invoker.sources, generator_js_outputs)
+ deps = [
+ ":$generator_target_name",
+ "//mojo/public/cpp/bindings",
+ ]
+ if (defined(invoker.deps)) {
+ deps += invoker.deps
+ }
+ if (defined(invoker.public_deps)) {
+ public_deps = invoker.public_deps
+ }
+ }
+}
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.gypi b/mojo/public/tools/bindings/mojom_bindings_generator.gypi
new file mode 100644
index 0000000..c6e9f98
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.gypi
@@ -0,0 +1,111 @@
+# 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.
+
+{
+ 'rules': [
+ {
+ 'rule_name': '<(_target_name)_mojom_bindings_generator',
+ 'extension': 'mojom',
+ 'variables': {
+ 'mojom_base_output_dir':
+ '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+ 'mojom_bindings_generator':
+ '<(DEPTH)/mojo/public/tools/bindings/mojom_bindings_generator.py',
+ 'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ 'mojom_import_args%': [
+ '-I<(DEPTH)'
+ ],
+ },
+ 'inputs': [
+ '<(mojom_bindings_generator)',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/struct_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/python_templates/module_macros.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/python_templates/module.py.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_cpp_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_java_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_js_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_python_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/error.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/data.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/module.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/pack.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/ast.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/parser.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/translate.py',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.h',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.js',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)_mojom.py',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-internal.h',
+ ],
+ 'action': [
+ 'python', '<@(mojom_bindings_generator)',
+ './<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+ '--use_chromium_bundled_pylibs',
+ '-d', '<(DEPTH)',
+ '<@(mojom_import_args)',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)',
+ '--java_output_directory=<(java_out_dir)',
+ ],
+ 'message': 'Generating Mojo bindings from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+ 'process_outputs_as_sources': 1,
+ }
+ ],
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'variables': {
+ 'generated_src_dirs': [
+ '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ ],
+ },
+ },
+ 'hard_dependency': 1,
+}
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
new file mode 100755
index 0000000..53e3a33
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# 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.
+
+"""The frontend for the Mojo bindings system."""
+
+
+import argparse
+import imp
+import os
+import pprint
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+# Manually check for the command-line flag. (This isn't quite right, since it
+# ignores, e.g., "--", but it's close enough.)
+if "--use_chromium_bundled_pylibs" in sys.argv[1:]:
+ sys.path.insert(0, os.path.join(_GetDirAbove("mojo"), "third_party"))
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "pylib"))
+
+from mojom.error import Error
+from mojom.generate.data import OrderedModuleFromData
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+def LoadGenerators(generators_string):
+ if not generators_string:
+ return [] # No generators.
+
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ generators = []
+ for generator_name in [s.strip() for s in generators_string.split(",")]:
+ # "Built-in" generators:
+ if generator_name.lower() == "c++":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_cpp_generator.py")
+ elif generator_name.lower() == "javascript":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_js_generator.py")
+ elif generator_name.lower() == "java":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_java_generator.py")
+ elif generator_name.lower() == "python":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_python_generator.py")
+ # Specified generator python module:
+ elif generator_name.endswith(".py"):
+ pass
+ else:
+ print "Unknown generator name %s" % generator_name
+ sys.exit(1)
+ generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
+ generator_name)
+ generators.append(generator_module)
+ return generators
+
+
+def MakeImportStackMessage(imported_filename_stack):
+ """Make a (human-readable) message listing a chain of imports. (Returned
+ string begins with a newline (if nonempty) and does not end with one.)"""
+ return ''.join(
+ reversed(["\n %s was imported by %s" % (a, b) for (a, b) in \
+ zip(imported_filename_stack[1:], imported_filename_stack)]))
+
+
+def FindImportFile(dir_name, file_name, search_dirs):
+ for search_dir in [dir_name] + search_dirs:
+ path = os.path.join(search_dir, file_name)
+ if os.path.isfile(path):
+ return path
+ return os.path.join(dir_name, file_name)
+
+
+# Disable check for dangerous default arguments (they're "private" keyword
+# arguments; note that we want |_processed_files| to memoize across invocations
+# of |ProcessFile()|):
+# pylint: disable=W0102
+def ProcessFile(args, remaining_args, generator_modules, filename,
+ _processed_files={}, _imported_filename_stack=None):
+ # Memoized results.
+ if filename in _processed_files:
+ return _processed_files[filename]
+
+ if _imported_filename_stack is None:
+ _imported_filename_stack = []
+
+ # Ensure we only visit each file once.
+ if filename in _imported_filename_stack:
+ print "%s: Error: Circular dependency" % filename + \
+ MakeImportStackMessage(_imported_filename_stack + [filename])
+ sys.exit(1)
+
+ try:
+ with open(filename) as f:
+ source = f.read()
+ except IOError as e:
+ print "%s: Error: %s" % (e.filename, e.strerror) + \
+ MakeImportStackMessage(_imported_filename_stack + [filename])
+ sys.exit(1)
+
+ try:
+ tree = Parse(source, filename)
+ except Error as e:
+ print str(e) + MakeImportStackMessage(_imported_filename_stack + [filename])
+ sys.exit(1)
+
+ dirname, name = os.path.split(filename)
+ mojom = Translate(tree, name)
+ if args.debug_print_intermediate:
+ pprint.PrettyPrinter().pprint(mojom)
+
+ # Process all our imports first and collect the module object for each.
+ # We use these to generate proper type info.
+ for import_data in mojom['imports']:
+ import_filename = FindImportFile(dirname,
+ import_data['filename'],
+ args.import_directories)
+ import_data['module'] = ProcessFile(
+ args, remaining_args, generator_modules, import_filename,
+ _processed_files=_processed_files,
+ _imported_filename_stack=_imported_filename_stack + [filename])
+
+ module = OrderedModuleFromData(mojom)
+
+ # Set the path as relative to the source root.
+ module.path = os.path.relpath(os.path.abspath(filename),
+ os.path.abspath(args.depth))
+
+ # Normalize to unix-style path here to keep the generators simpler.
+ module.path = module.path.replace('\\', '/')
+
+ for generator_module in generator_modules:
+ generator = generator_module.Generator(module, args.output_dir)
+ filtered_args = []
+ if hasattr(generator_module, 'GENERATOR_PREFIX'):
+ prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
+ filtered_args = [arg for arg in remaining_args if arg.startswith(prefix)]
+ generator.GenerateFiles(filtered_args)
+
+ # Save result.
+ _processed_files[filename] = module
+ return module
+# pylint: enable=W0102
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generate bindings from mojom files.")
+ parser.add_argument("filename", nargs="+",
+ help="mojom input file")
+ parser.add_argument("-d", "--depth", dest="depth", default=".",
+ help="depth from source root")
+ parser.add_argument("-o", "--output_dir", dest="output_dir", default=".",
+ help="output directory for generated files")
+ parser.add_argument("-g", "--generators", dest="generators_string",
+ metavar="GENERATORS",
+ default="c++,javascript,java,python",
+ help="comma-separated list of generators")
+ parser.add_argument("--debug_print_intermediate", action="store_true",
+ help="print the intermediate representation")
+ parser.add_argument("-I", dest="import_directories", action="append",
+ metavar="directory", default=[],
+ help="add a directory to be searched for import files")
+ parser.add_argument("--use_chromium_bundled_pylibs", action="store_true",
+ help="use Python modules bundled in the Chromium source")
+ (args, remaining_args) = parser.parse_known_args()
+
+ generator_modules = LoadGenerators(args.generators_string)
+
+ if not os.path.exists(args.output_dir):
+ os.makedirs(args.output_dir)
+
+ for filename in args.filename:
+ ProcessFile(args, remaining_args, generator_modules, filename)
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py b/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
new file mode 100644
index 0000000..de38856
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
@@ -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.
+
+import unittest
+
+from mojom_bindings_generator import MakeImportStackMessage
+
+
+class MojoBindingsGeneratorTest(unittest.TestCase):
+ """Tests mojo_bindings_generator."""
+
+ def testMakeImportStackMessage(self):
+ """Tests MakeImportStackMessage()."""
+ self.assertEquals(MakeImportStackMessage(["x"]), "")
+ self.assertEquals(MakeImportStackMessage(["x", "y"]),
+ "\n y was imported by x")
+ self.assertEquals(MakeImportStackMessage(["x", "y", "z"]),
+ "\n z was imported by y\n y was imported by x")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/mojo/public/tools/bindings/pylib/mojom/__init__.py b/mojo/public/tools/bindings/pylib/mojom/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/error.py b/mojo/public/tools/bindings/pylib/mojom/error.py
new file mode 100644
index 0000000..99522b9
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/error.py
@@ -0,0 +1,27 @@
+# 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.
+
+class Error(Exception):
+ """Base class for Mojo IDL bindings parser/generator errors."""
+
+ def __init__(self, filename, message, lineno=None, addenda=None, **kwargs):
+ """|filename| is the (primary) file which caused the error, |message| is the
+ error message, |lineno| is the 1-based line number (or |None| if not
+ applicable/available), and |addenda| is a list of additional lines to append
+ to the final error message."""
+ Exception.__init__(self, **kwargs)
+ self.filename = filename
+ self.message = message
+ self.lineno = lineno
+ self.addenda = addenda
+
+ def __str__(self):
+ if self.lineno:
+ s = "%s:%d: Error: %s" % (self.filename, self.lineno, self.message)
+ else:
+ s = "%s: Error: %s" % (self.filename, self.message)
+ return "\n".join([s] + self.addenda) if self.addenda else s
+
+ def __repr__(self):
+ return str(self)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
new file mode 100644
index 0000000..fab7e2e
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -0,0 +1,383 @@
+# 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.
+
+# TODO(vtl): "data" is a pretty vague name. Rename it?
+
+import copy
+
+import module as mojom
+
+# This module provides a mechanism to turn mojom Modules to dictionaries and
+# back again. This can be used to persist a mojom Module created progromatically
+# or to read a dictionary from code or a file.
+# Example:
+# test_dict = {
+# 'name': 'test',
+# 'namespace': 'testspace',
+# 'structs': [{
+# 'name': 'teststruct',
+# 'fields': [
+# {'name': 'testfield1', 'kind': 'i32'},
+# {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+# 'interfaces': [{
+# 'name': 'Server',
+# 'methods': [{
+# 'name': 'Foo',
+# 'parameters': [{
+# 'name': 'foo', 'kind': 'i32'},
+# {'name': 'bar', 'kind': 'a:x:teststruct'}],
+# 'ordinal': 42}]}]
+# }
+# test_module = data.ModuleFromData(test_dict)
+
+# Used to create a subclass of str that supports sorting by index, to make
+# pretty printing maintain the order.
+def istr(index, string):
+ class IndexedString(str):
+ def __lt__(self, other):
+ return self.__index__ < other.__index__
+
+ rv = IndexedString(string)
+ rv.__index__ = index
+ return rv
+
+builtin_values = frozenset([
+ "double.INFINITY",
+ "double.NEGATIVE_INFINITY",
+ "double.NAN",
+ "float.INFINITY",
+ "float.NEGATIVE_INFINITY",
+ "float.NAN"])
+
+def IsBuiltinValue(value):
+ return value in builtin_values
+
+def LookupKind(kinds, spec, scope):
+ """Tries to find which Kind a spec refers to, given the scope in which its
+ referenced. Starts checking from the narrowest scope to most general. For
+ example, given a struct field like
+ Foo.Bar x;
+ Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner
+ type 'Bar' in the struct 'Foo' in the current namespace.
+
+ |scope| is a tuple that looks like (namespace, struct/interface), referring
+ to the location where the type is referenced."""
+ if spec.startswith('x:'):
+ name = spec[2:]
+ for i in xrange(len(scope), -1, -1):
+ test_spec = 'x:'
+ if i > 0:
+ test_spec += '.'.join(scope[:i]) + '.'
+ test_spec += name
+ kind = kinds.get(test_spec)
+ if kind:
+ return kind
+
+ return kinds.get(spec)
+
+def LookupValue(values, name, scope, kind):
+ """Like LookupKind, but for constant values."""
+ # If the type is an enum, the value can be specified as a qualified name, in
+ # which case the form EnumName.ENUM_VALUE must be used. We use the presence
+ # of a '.' in the requested name to identify this. Otherwise, we prepend the
+ # enum name.
+ if isinstance(kind, mojom.Enum) and '.' not in name:
+ name = '%s.%s' % (kind.spec.split(':', 1)[1], name)
+ for i in reversed(xrange(len(scope) + 1)):
+ test_spec = '.'.join(scope[:i])
+ if test_spec:
+ test_spec += '.'
+ test_spec += name
+ value = values.get(test_spec)
+ if value:
+ return value
+
+ return values.get(name)
+
+def FixupExpression(module, value, scope, kind):
+ """Translates an IDENTIFIER into a built-in value or structured NamedValue
+ object."""
+ if isinstance(value, tuple) and value[0] == 'IDENTIFIER':
+ # Allow user defined values to shadow builtins.
+ result = LookupValue(module.values, value[1], scope, kind)
+ if result:
+ if isinstance(result, tuple):
+ raise Exception('Unable to resolve expression: %r' % value[1])
+ return result
+ if IsBuiltinValue(value[1]):
+ return mojom.BuiltinValue(value[1])
+ return value
+
+def KindToData(kind):
+ return kind.spec
+
+def KindFromData(kinds, data, scope):
+ kind = LookupKind(kinds, data, scope)
+ if kind:
+ return kind
+
+ if data.startswith('?'):
+ kind = KindFromData(kinds, data[1:], scope).MakeNullableKind()
+ elif data.startswith('a:'):
+ kind = mojom.Array(KindFromData(kinds, data[2:], scope))
+ elif data.startswith('r:'):
+ kind = mojom.InterfaceRequest(KindFromData(kinds, data[2:], scope))
+ elif data.startswith('a'):
+ colon = data.find(':')
+ length = int(data[1:colon])
+ kind = mojom.FixedArray(length, KindFromData(kinds, data[colon+1:], scope))
+ else:
+ kind = mojom.Kind(data)
+
+ kinds[data] = kind
+ return kind
+
+def KindFromImport(original_kind, imported_from):
+ """Used with 'import module' - clones the kind imported from the given
+ module's namespace. Only used with Structs, Interfaces and Enums."""
+ kind = copy.copy(original_kind)
+ # |shared_definition| is used to store various properties (see
+ # |AddSharedProperty()| in module.py), including |imported_from|. We don't
+ # want the copy to share these with the original, so copy it if necessary.
+ if hasattr(original_kind, 'shared_definition'):
+ kind.shared_definition = copy.copy(original_kind.shared_definition)
+ kind.imported_from = imported_from
+ return kind
+
+def ImportFromData(module, data):
+ import_module = data['module']
+
+ import_item = {}
+ import_item['module_name'] = import_module.name
+ import_item['namespace'] = import_module.namespace
+ import_item['module'] = import_module
+
+ # Copy the struct kinds from our imports into the current module.
+ for kind in import_module.kinds.itervalues():
+ if (isinstance(kind, (mojom.Struct, mojom.Enum, mojom.Interface)) and
+ kind.imported_from is None):
+ kind = KindFromImport(kind, import_item)
+ module.kinds[kind.spec] = kind
+ # Ditto for values.
+ for value in import_module.values.itervalues():
+ if value.imported_from is None:
+ # Values don't have shared definitions (since they're not nullable), so no
+ # need to do anything special.
+ value = copy.copy(value)
+ value.imported_from = import_item
+ module.values[value.GetSpec()] = value
+
+ return import_item
+
+def StructToData(struct):
+ return {
+ istr(0, 'name'): struct.name,
+ istr(1, 'fields'): map(FieldToData, struct.fields)
+ }
+
+def StructFromData(module, data):
+ struct = mojom.Struct(module=module)
+ struct.name = data['name']
+ struct.attributes = data['attributes']
+ struct.spec = 'x:' + module.namespace + '.' + struct.name
+ module.kinds[struct.spec] = struct
+ struct.enums = map(lambda enum:
+ EnumFromData(module, enum, struct), data['enums'])
+ struct.constants = map(lambda constant:
+ ConstantFromData(module, constant, struct), data['constants'])
+ # Stash fields data here temporarily.
+ struct.fields_data = data['fields']
+ return struct
+
+def FieldToData(field):
+ data = {
+ istr(0, 'name'): field.name,
+ istr(1, 'kind'): KindToData(field.kind)
+ }
+ if field.ordinal != None:
+ data[istr(2, 'ordinal')] = field.ordinal
+ if field.default != None:
+ data[istr(3, 'default')] = field.default
+ return data
+
+def FieldFromData(module, data, struct):
+ field = mojom.Field()
+ field.name = data['name']
+ field.kind = KindFromData(
+ module.kinds, data['kind'], (module.namespace, struct.name))
+ field.ordinal = data.get('ordinal')
+ field.default = FixupExpression(
+ module, data.get('default'), (module.namespace, struct.name), field.kind)
+ return field
+
+def ParameterToData(parameter):
+ data = {
+ istr(0, 'name'): parameter.name,
+ istr(1, 'kind'): parameter.kind.spec
+ }
+ if parameter.ordinal != None:
+ data[istr(2, 'ordinal')] = parameter.ordinal
+ if parameter.default != None:
+ data[istr(3, 'default')] = parameter.default
+ return data
+
+def ParameterFromData(module, data, interface):
+ parameter = mojom.Parameter()
+ parameter.name = data['name']
+ parameter.kind = KindFromData(
+ module.kinds, data['kind'], (module.namespace, interface.name))
+ parameter.ordinal = data.get('ordinal')
+ parameter.default = data.get('default')
+ return parameter
+
+def MethodToData(method):
+ data = {
+ istr(0, 'name'): method.name,
+ istr(1, 'parameters'): map(ParameterToData, method.parameters)
+ }
+ if method.ordinal != None:
+ data[istr(2, 'ordinal')] = method.ordinal
+ if method.response_parameters != None:
+ data[istr(3, 'response_parameters')] = map(
+ ParameterToData, method.response_parameters)
+ return data
+
+def MethodFromData(module, data, interface):
+ method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal'))
+ method.default = data.get('default')
+ method.parameters = map(lambda parameter:
+ ParameterFromData(module, parameter, interface), data['parameters'])
+ if data.has_key('response_parameters'):
+ method.response_parameters = map(
+ lambda parameter: ParameterFromData(module, parameter, interface),
+ data['response_parameters'])
+ return method
+
+def InterfaceToData(interface):
+ return {
+ istr(0, 'name'): interface.name,
+ istr(1, 'client'): interface.client,
+ istr(2, 'methods'): map(MethodToData, interface.methods)
+ }
+
+def InterfaceFromData(module, data):
+ interface = mojom.Interface(module=module)
+ interface.name = data['name']
+ interface.spec = 'x:' + module.namespace + '.' + interface.name
+ interface.client = data['client'] if data.has_key('client') else None
+ module.kinds[interface.spec] = interface
+ interface.enums = map(lambda enum:
+ EnumFromData(module, enum, interface), data['enums'])
+ interface.constants = map(lambda constant:
+ ConstantFromData(module, constant, interface), data['constants'])
+ # Stash methods data here temporarily.
+ interface.methods_data = data['methods']
+ return interface
+
+def EnumFieldFromData(module, enum, data, parent_kind):
+ field = mojom.EnumField()
+ field.name = data['name']
+ # TODO(mpcomplete): FixupExpression should be done in the second pass,
+ # so constants and enums can refer to each other.
+ # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or
+ # vice versa?
+ if parent_kind:
+ field.value = FixupExpression(
+ module, data['value'], (module.namespace, parent_kind.name), enum)
+ else:
+ field.value = FixupExpression(
+ module, data['value'], (module.namespace, ), enum)
+ value = mojom.EnumValue(module, enum, field)
+ module.values[value.GetSpec()] = value
+ return field
+
+def EnumFromData(module, data, parent_kind):
+ enum = mojom.Enum(module=module)
+ enum.name = data['name']
+ name = enum.name
+ if parent_kind:
+ name = parent_kind.name + '.' + name
+ enum.spec = 'x:%s.%s' % (module.namespace, name)
+ enum.parent_kind = parent_kind
+
+ enum.fields = map(
+ lambda field: EnumFieldFromData(module, enum, field, parent_kind),
+ data['fields'])
+ module.kinds[enum.spec] = enum
+ return enum
+
+def ConstantFromData(module, data, parent_kind):
+ constant = mojom.Constant()
+ constant.name = data['name']
+ if parent_kind:
+ scope = (module.namespace, parent_kind.name)
+ else:
+ scope = (module.namespace, )
+ # TODO(mpcomplete): maybe we should only support POD kinds.
+ constant.kind = KindFromData(module.kinds, data['kind'], scope)
+ constant.value = FixupExpression(module, data.get('value'), scope, None)
+
+ value = mojom.ConstantValue(module, parent_kind, constant)
+ module.values[value.GetSpec()] = value
+ return constant
+
+def ModuleToData(module):
+ return {
+ istr(0, 'name'): module.name,
+ istr(1, 'namespace'): module.namespace,
+ istr(2, 'structs'): map(StructToData, module.structs),
+ istr(3, 'interfaces'): map(InterfaceToData, module.interfaces)
+ }
+
+def ModuleFromData(data):
+ module = mojom.Module()
+ module.kinds = {}
+ for kind in mojom.PRIMITIVES:
+ module.kinds[kind.spec] = kind
+
+ module.values = {}
+
+ module.name = data['name']
+ module.namespace = data['namespace']
+ module.attributes = data['attributes']
+ # Imports must come first, because they add to module.kinds which is used
+ # by by the others.
+ module.imports = map(
+ lambda import_data: ImportFromData(module, import_data),
+ data['imports'])
+
+ # First pass collects kinds.
+ module.enums = map(
+ lambda enum: EnumFromData(module, enum, None), data['enums'])
+ module.structs = map(
+ lambda struct: StructFromData(module, struct), data['structs'])
+ module.interfaces = map(
+ lambda interface: InterfaceFromData(module, interface),
+ data['interfaces'])
+ module.constants = map(
+ lambda constant: ConstantFromData(module, constant, None),
+ data['constants'])
+
+ # Second pass expands fields and methods. This allows fields and parameters
+ # to refer to kinds defined anywhere in the mojom.
+ for struct in module.structs:
+ struct.fields = map(lambda field:
+ FieldFromData(module, field, struct), struct.fields_data)
+ del struct.fields_data
+ for interface in module.interfaces:
+ interface.methods = map(lambda method:
+ MethodFromData(module, method, interface), interface.methods_data)
+ del interface.methods_data
+
+ return module
+
+def OrderedModuleFromData(data):
+ module = ModuleFromData(data)
+ for interface in module.interfaces:
+ next_ordinal = 0
+ for method in interface.methods:
+ if method.ordinal is None:
+ method.ordinal = next_ordinal
+ next_ordinal = method.ordinal + 1
+ return module
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
new file mode 100644
index 0000000..096554c
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
@@ -0,0 +1,86 @@
+# 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.
+
+import sys
+
+import data
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def DeepEquals(d1, d2):
+ if d1 == d2:
+ return True
+ if d2.__class__ != d2.__class__:
+ return False
+ if isinstance(d1, dict):
+ if set(d1.keys()) != set(d2.keys()):
+ return False
+ for key in d1.keys():
+ if not DeepEquals(d1[key], d2[key]):
+ return False
+ return True
+ if isinstance(d1, (list, tuple)):
+ if len(d1) != len(d2):
+ return False
+ for i in range(len(d1)):
+ if not DeepEquals(d1[i], d2[i]):
+ return False
+ return True
+ return False
+
+
+test_dict = {
+ 'name': 'test',
+ 'namespace': 'testspace',
+ 'structs': [{
+ 'name': 'teststruct',
+ 'fields': [
+ {'name': 'testfield1', 'kind': 'i32'},
+ {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+ 'interfaces': [{
+ 'name': 'Server',
+ 'client': None,
+ 'methods': [{
+ 'name': 'Foo',
+ 'parameters': [
+ {'name': 'foo', 'kind': 'i32'},
+ {'name': 'bar', 'kind': 'a:x:teststruct'}],
+ 'ordinal': 42}]}]
+}
+
+
+def TestRead():
+ module = data.ModuleFromData(test_dict)
+ return test_support.TestTestModule(module)
+
+
+def TestWrite():
+ module = test_support.BuildTestModule()
+ d = data.ModuleToData(module)
+ return EXPECT_TRUE(DeepEquals(test_dict, d))
+
+
+def TestWriteRead():
+ module1 = test_support.BuildTestModule()
+
+ dict1 = data.ModuleToData(module1)
+ module2 = data.ModuleFromData(dict1)
+ return EXPECT_TRUE(test_support.ModulesAreEqual(module1, module2))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(TestWriteRead)
+ errors += RunTest(TestRead)
+ errors += RunTest(TestWrite)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
new file mode 100644
index 0000000..eb433bf
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -0,0 +1,85 @@
+# 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.
+
+"""Code shared by the various language-specific code generators."""
+
+from functools import partial
+import os.path
+import re
+
+import module as mojom
+import pack
+
+def GetStructFromMethod(method):
+ """Converts a method's parameters into the fields of a struct."""
+ params_class = "%s_%s_Params" % (method.interface.name, method.name)
+ struct = mojom.Struct(params_class, module=method.interface.module)
+ for param in method.parameters:
+ struct.AddField(param.name, param.kind, param.ordinal)
+ struct.packed = pack.PackedStruct(struct)
+ return struct
+
+def GetResponseStructFromMethod(method):
+ """Converts a method's response_parameters into the fields of a struct."""
+ params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
+ struct = mojom.Struct(params_class, module=method.interface.module)
+ for param in method.response_parameters:
+ struct.AddField(param.name, param.kind, param.ordinal)
+ struct.packed = pack.PackedStruct(struct)
+ return struct
+
+def GetDataHeader(exported, struct):
+ struct.packed = pack.PackedStruct(struct)
+ struct.bytes = pack.GetByteLayout(struct.packed)
+ struct.exported = exported
+ return struct
+
+def ExpectedArraySize(kind):
+ if mojom.IsFixedArrayKind(kind):
+ return kind.length
+ return 0
+
+def StudlyCapsToCamel(studly):
+ return studly[0].lower() + studly[1:]
+
+def CamelCaseToAllCaps(camel_case):
+ return '_'.join(
+ word for word in re.split(r'([A-Z][^A-Z]+)', camel_case) if word).upper()
+
+class Generator(object):
+ # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
+ # files to stdout.
+ def __init__(self, module, output_dir=None):
+ self.module = module
+ self.output_dir = output_dir
+
+ def GetStructsFromMethods(self):
+ result = []
+ for interface in self.module.interfaces:
+ for method in interface.methods:
+ result.append(GetStructFromMethod(method))
+ if method.response_parameters != None:
+ result.append(GetResponseStructFromMethod(method))
+ return map(partial(GetDataHeader, False), result)
+
+ def GetStructs(self):
+ return map(partial(GetDataHeader, True), self.module.structs)
+
+ def Write(self, contents, filename):
+ if self.output_dir is None:
+ print contents
+ return
+ with open(os.path.join(self.output_dir, filename), "w+") as f:
+ f.write(contents)
+
+ def GenerateFiles(self, args):
+ raise NotImplementedError("Subclasses must override/implement this method")
+
+ def GetJinjaParameters(self):
+ """Returns default constructor parameters for the jinja environment."""
+ return {}
+
+ def GetGlobals(self):
+ """Returns global mappings for the template generation."""
+ return {}
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
new file mode 100644
index 0000000..7ae7a83
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -0,0 +1,448 @@
+# 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.
+
+# This module's classes provide an interface to mojo modules. Modules are
+# collections of interfaces and structs to be used by mojo ipc clients and
+# servers.
+#
+# A simple interface would be created this way:
+# module = mojom.generate.module.Module('Foo')
+# interface = module.AddInterface('Bar')
+# method = interface.AddMethod('Tat', 0)
+# method.AddParameter('baz', 0, mojom.INT32)
+
+
+class Kind(object):
+ def __init__(self, spec=None):
+ self.spec = spec
+ self.parent_kind = None
+
+
+class ReferenceKind(Kind):
+ """ReferenceKind represents pointer types and handle types.
+ A type is nullable if null (for pointer types) or invalid handle (for handle
+ types) is a legal value for the type.
+ """
+
+ def __init__(self, spec=None, is_nullable=False):
+ assert spec is None or is_nullable == spec.startswith('?')
+ Kind.__init__(self, spec)
+ self.is_nullable = is_nullable
+ self.shared_definition = {}
+
+ def MakeNullableKind(self):
+ assert not self.is_nullable
+
+ if self == STRING:
+ return NULLABLE_STRING
+ if self == HANDLE:
+ return NULLABLE_HANDLE
+ if self == DCPIPE:
+ return NULLABLE_DCPIPE
+ if self == DPPIPE:
+ return NULLABLE_DPPIPE
+ if self == MSGPIPE:
+ return NULLABLE_MSGPIPE
+ if self == SHAREDBUFFER:
+ return NULLABLE_SHAREDBUFFER
+
+ nullable_kind = type(self)()
+ nullable_kind.shared_definition = self.shared_definition
+ if self.spec is not None:
+ nullable_kind.spec = '?' + self.spec
+ nullable_kind.is_nullable = True
+
+ return nullable_kind
+
+ @classmethod
+ def AddSharedProperty(cls, name):
+ """Adds a property |name| to |cls|, which accesses the corresponding item in
+ |shared_definition|.
+
+ The reason of adding such indirection is to enable sharing definition
+ between a reference kind and its nullable variation. For example:
+ a = Struct('test_struct_1')
+ b = a.MakeNullableKind()
+ a.name = 'test_struct_2'
+ print b.name # Outputs 'test_struct_2'.
+ """
+ def Get(self):
+ return self.shared_definition[name]
+
+ def Set(self, value):
+ self.shared_definition[name] = value
+
+ setattr(cls, name, property(Get, Set))
+
+
+# Initialize the set of primitive types. These can be accessed by clients.
+BOOL = Kind('b')
+INT8 = Kind('i8')
+INT16 = Kind('i16')
+INT32 = Kind('i32')
+INT64 = Kind('i64')
+UINT8 = Kind('u8')
+UINT16 = Kind('u16')
+UINT32 = Kind('u32')
+UINT64 = Kind('u64')
+FLOAT = Kind('f')
+DOUBLE = Kind('d')
+STRING = ReferenceKind('s')
+HANDLE = ReferenceKind('h')
+DCPIPE = ReferenceKind('h:d:c')
+DPPIPE = ReferenceKind('h:d:p')
+MSGPIPE = ReferenceKind('h:m')
+SHAREDBUFFER = ReferenceKind('h:s')
+NULLABLE_STRING = ReferenceKind('?s', True)
+NULLABLE_HANDLE = ReferenceKind('?h', True)
+NULLABLE_DCPIPE = ReferenceKind('?h:d:c', True)
+NULLABLE_DPPIPE = ReferenceKind('?h:d:p', True)
+NULLABLE_MSGPIPE = ReferenceKind('?h:m', True)
+NULLABLE_SHAREDBUFFER = ReferenceKind('?h:s', True)
+
+
+# Collection of all Primitive types
+PRIMITIVES = (
+ BOOL,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+ UINT8,
+ UINT16,
+ UINT32,
+ UINT64,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ HANDLE,
+ DCPIPE,
+ DPPIPE,
+ MSGPIPE,
+ SHAREDBUFFER,
+ NULLABLE_STRING,
+ NULLABLE_HANDLE,
+ NULLABLE_DCPIPE,
+ NULLABLE_DPPIPE,
+ NULLABLE_MSGPIPE,
+ NULLABLE_SHAREDBUFFER
+)
+
+
+class NamedValue(object):
+ def __init__(self, module, parent_kind, name):
+ self.module = module
+ self.namespace = module.namespace
+ self.parent_kind = parent_kind
+ self.name = name
+ self.imported_from = None
+
+ def GetSpec(self):
+ return (self.namespace + '.' +
+ (self.parent_kind and (self.parent_kind.name + '.') or "") +
+ self.name)
+
+
+class BuiltinValue(object):
+ def __init__(self, value):
+ self.value = value
+
+
+class ConstantValue(NamedValue):
+ def __init__(self, module, parent_kind, constant):
+ NamedValue.__init__(self, module, parent_kind, constant.name)
+ self.constant = constant
+
+
+class EnumValue(NamedValue):
+ def __init__(self, module, enum, field):
+ NamedValue.__init__(self, module, enum.parent_kind, field.name)
+ self.enum = enum
+
+ def GetSpec(self):
+ return (self.namespace + '.' +
+ (self.parent_kind and (self.parent_kind.name + '.') or "") +
+ self.enum.name + '.' + self.name)
+
+
+class Constant(object):
+ def __init__(self, name=None, kind=None, value=None):
+ self.name = name
+ self.kind = kind
+ self.value = value
+
+
+class Field(object):
+ def __init__(self, name=None, kind=None, ordinal=None, default=None):
+ self.name = name
+ self.kind = kind
+ self.ordinal = ordinal
+ self.default = default
+
+
+class Struct(ReferenceKind):
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('module')
+ ReferenceKind.AddSharedProperty('imported_from')
+ ReferenceKind.AddSharedProperty('fields')
+
+ def __init__(self, name=None, module=None):
+ if name is not None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ ReferenceKind.__init__(self, spec)
+ self.name = name
+ self.module = module
+ self.imported_from = None
+ self.fields = []
+
+ def AddField(self, name, kind, ordinal=None, default=None):
+ field = Field(name, kind, ordinal, default)
+ self.fields.append(field)
+ return field
+
+
+class Array(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ ReferenceKind.__init__(self, 'a:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+
+class FixedArray(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+ ReferenceKind.AddSharedProperty('length')
+
+ def __init__(self, length=-1, kind=None):
+ if kind is not None:
+ ReferenceKind.__init__(self, 'a%d:%s' % (length, kind.spec))
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+ self.length = length
+
+
+class InterfaceRequest(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ ReferenceKind.__init__(self, 'r:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+
+class Parameter(object):
+ def __init__(self, name=None, kind=None, ordinal=None, default=None):
+ self.name = name
+ self.ordinal = ordinal
+ self.kind = kind
+ self.default = default
+
+
+class Method(object):
+ def __init__(self, interface, name, ordinal=None):
+ self.interface = interface
+ self.name = name
+ self.ordinal = ordinal
+ self.parameters = []
+ self.response_parameters = None
+
+ def AddParameter(self, name, kind, ordinal=None, default=None):
+ parameter = Parameter(name, kind, ordinal, default)
+ self.parameters.append(parameter)
+ return parameter
+
+ def AddResponseParameter(self, name, kind, ordinal=None, default=None):
+ if self.response_parameters == None:
+ self.response_parameters = []
+ parameter = Parameter(name, kind, ordinal, default)
+ self.response_parameters.append(parameter)
+ return parameter
+
+
+class Interface(ReferenceKind):
+ ReferenceKind.AddSharedProperty('module')
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('imported_from')
+ ReferenceKind.AddSharedProperty('client')
+ ReferenceKind.AddSharedProperty('methods')
+
+ def __init__(self, name=None, client=None, module=None):
+ if name is not None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ ReferenceKind.__init__(self, spec)
+ self.module = module
+ self.name = name
+ self.imported_from = None
+ self.client = client
+ self.methods = []
+
+ def AddMethod(self, name, ordinal=None):
+ method = Method(self, name, ordinal=ordinal)
+ self.methods.append(method)
+ return method
+
+
+class EnumField(object):
+ def __init__(self, name=None, value=None):
+ self.name = name
+ self.value = value
+
+
+class Enum(Kind):
+ def __init__(self, name=None, module=None):
+ self.module = module
+ self.name = name
+ self.imported_from = None
+ if name is not None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ Kind.__init__(self, spec)
+ self.fields = []
+
+
+class Module(object):
+ def __init__(self, name=None, namespace=None):
+ self.name = name
+ self.path = name
+ self.namespace = namespace
+ self.structs = []
+ self.interfaces = []
+
+ def AddInterface(self, name):
+ self.interfaces.append(Interface(name, module=self))
+ return interface
+
+ def AddStruct(self, name):
+ struct=Struct(name, module=self)
+ self.structs.append(struct)
+ return struct
+
+
+def IsBoolKind(kind):
+ return kind.spec == BOOL.spec
+
+
+def IsFloatKind(kind):
+ return kind.spec == FLOAT.spec
+
+
+def IsStringKind(kind):
+ return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec
+
+
+def IsHandleKind(kind):
+ return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec
+
+
+def IsDataPipeConsumerKind(kind):
+ return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec
+
+
+def IsDataPipeProducerKind(kind):
+ return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec
+
+
+def IsMessagePipeKind(kind):
+ return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec
+
+
+def IsSharedBufferKind(kind):
+ return (kind.spec == SHAREDBUFFER.spec or
+ kind.spec == NULLABLE_SHAREDBUFFER.spec)
+
+
+def IsStructKind(kind):
+ return isinstance(kind, Struct)
+
+
+def IsArrayKind(kind):
+ return isinstance(kind, Array)
+
+
+def IsFixedArrayKind(kind):
+ return isinstance(kind, FixedArray)
+
+
+def IsInterfaceKind(kind):
+ return isinstance(kind, Interface)
+
+
+def IsInterfaceRequestKind(kind):
+ return isinstance(kind, InterfaceRequest)
+
+
+def IsEnumKind(kind):
+ return isinstance(kind, Enum)
+
+
+def IsReferenceKind(kind):
+ return isinstance(kind, ReferenceKind)
+
+
+def IsNullableKind(kind):
+ return IsReferenceKind(kind) and kind.is_nullable
+
+
+def IsAnyArrayKind(kind):
+ return IsArrayKind(kind) or IsFixedArrayKind(kind)
+
+
+def IsObjectKind(kind):
+ return IsStructKind(kind) or IsAnyArrayKind(kind) or IsStringKind(kind)
+
+
+def IsNonInterfaceHandleKind(kind):
+ return (IsHandleKind(kind) or
+ IsDataPipeConsumerKind(kind) or
+ IsDataPipeProducerKind(kind) or
+ IsMessagePipeKind(kind) or
+ IsSharedBufferKind(kind))
+
+
+def IsAnyHandleKind(kind):
+ return (IsNonInterfaceHandleKind(kind) or
+ IsInterfaceKind(kind) or
+ IsInterfaceRequestKind(kind))
+
+
+def IsMoveOnlyKind(kind):
+ return IsObjectKind(kind) or IsAnyHandleKind(kind)
+
+
+def IsCloneableKind(kind):
+ def ContainsHandles(kind, visited_kinds):
+ if kind in visited_kinds:
+ # No need to examine the kind again.
+ return False
+ visited_kinds.add(kind)
+ if IsAnyHandleKind(kind):
+ return True
+ if IsAnyArrayKind(kind):
+ return ContainsHandles(kind.kind, visited_kinds)
+ if IsStructKind(kind):
+ for field in kind.fields:
+ if ContainsHandles(field.kind, visited_kinds):
+ return True
+ return False
+
+ return not ContainsHandles(kind, set())
+
+
+def HasCallbacks(interface):
+ for method in interface.methods:
+ if method.response_parameters != None:
+ return True
+ return False
+
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
new file mode 100644
index 0000000..a887686
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
@@ -0,0 +1,34 @@
+# 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.
+
+import sys
+
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+ModulesAreEqual = test_support.ModulesAreEqual
+BuildTestModule = test_support.BuildTestModule
+TestTestModule = test_support.TestTestModule
+
+
+def BuildAndTestModule():
+ return TestTestModule(BuildTestModule())
+
+
+def TestModulesEqual():
+ return EXPECT_TRUE(ModulesAreEqual(BuildTestModule(), BuildTestModule()))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(BuildAndTestModule)
+ errors += RunTest(TestModulesEqual)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
new file mode 100644
index 0000000..4661941
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
@@ -0,0 +1,163 @@
+# 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.
+
+import module as mojom
+
+# This module provides a mechanism for determining the packed order and offsets
+# of a mojom.Struct.
+#
+# ps = pack.PackedStruct(struct)
+# ps.packed_fields will access a list of PackedField objects, each of which
+# will have an offset, a size and a bit (for mojom.BOOLs).
+
+class PackedField(object):
+ kind_to_size = {
+ mojom.BOOL: 1,
+ mojom.INT8: 1,
+ mojom.UINT8: 1,
+ mojom.INT16: 2,
+ mojom.UINT16: 2,
+ mojom.INT32: 4,
+ mojom.UINT32: 4,
+ mojom.FLOAT: 4,
+ mojom.HANDLE: 4,
+ mojom.MSGPIPE: 4,
+ mojom.SHAREDBUFFER: 4,
+ mojom.DCPIPE: 4,
+ mojom.DPPIPE: 4,
+ mojom.NULLABLE_HANDLE: 4,
+ mojom.NULLABLE_MSGPIPE: 4,
+ mojom.NULLABLE_SHAREDBUFFER: 4,
+ mojom.NULLABLE_DCPIPE: 4,
+ mojom.NULLABLE_DPPIPE: 4,
+ mojom.INT64: 8,
+ mojom.UINT64: 8,
+ mojom.DOUBLE: 8,
+ mojom.STRING: 8,
+ mojom.NULLABLE_STRING: 8
+ }
+
+ @classmethod
+ def GetSizeForKind(cls, kind):
+ if isinstance(kind, (mojom.Array, mojom.Struct, mojom.FixedArray)):
+ return 8
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ kind = mojom.MSGPIPE
+ if isinstance(kind, mojom.Enum):
+ # TODO(mpcomplete): what about big enums?
+ return cls.kind_to_size[mojom.INT32]
+ if not kind in cls.kind_to_size:
+ raise Exception("Invalid kind: %s" % kind.spec)
+ return cls.kind_to_size[kind]
+
+ def __init__(self, field, index, ordinal):
+ """
+ Args:
+ field: the original field.
+ index: the position of the original field in the struct.
+ ordinal: the ordinal of the field for serialization.
+ """
+ self.field = field
+ self.index = index
+ self.ordinal = ordinal
+ self.size = self.GetSizeForKind(field.kind)
+ self.offset = None
+ self.bit = None
+
+
+# Returns the pad necessary to reserve space for alignment of |size|.
+def GetPad(offset, size):
+ return (size - (offset % size)) % size
+
+
+# Returns a 2-tuple of the field offset and bit (for BOOLs)
+def GetFieldOffset(field, last_field):
+ if field.field.kind == mojom.BOOL and \
+ last_field.field.kind == mojom.BOOL and \
+ last_field.bit < 7:
+ return (last_field.offset, last_field.bit + 1)
+
+ offset = last_field.offset + last_field.size
+ pad = GetPad(offset, field.size)
+ return (offset + pad, 0)
+
+
+class PackedStruct(object):
+ def __init__(self, struct):
+ self.struct = struct
+ self.packed_fields = []
+
+ # No fields.
+ if (len(struct.fields) == 0):
+ return
+
+ # Start by sorting by ordinal.
+ src_fields = []
+ ordinal = 0
+ for index, field in enumerate(struct.fields):
+ if field.ordinal is not None:
+ ordinal = field.ordinal
+ src_fields.append(PackedField(field, index, ordinal))
+ ordinal += 1
+ src_fields.sort(key=lambda field: field.ordinal)
+
+ src_field = src_fields[0]
+ src_field.offset = 0
+ src_field.bit = 0
+ # dst_fields will contain each of the fields, in increasing offset order.
+ dst_fields = self.packed_fields
+ dst_fields.append(src_field)
+
+ # Then find first slot that each field will fit.
+ for src_field in src_fields[1:]:
+ last_field = dst_fields[0]
+ for i in xrange(1, len(dst_fields)):
+ next_field = dst_fields[i]
+ offset, bit = GetFieldOffset(src_field, last_field)
+ if offset + src_field.size <= next_field.offset:
+ # Found hole.
+ src_field.offset = offset
+ src_field.bit = bit
+ dst_fields.insert(i, src_field)
+ break
+ last_field = next_field
+ if src_field.offset is None:
+ # Add to end
+ src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
+ dst_fields.append(src_field)
+
+ def GetTotalSize(self):
+ if not self.packed_fields:
+ return 0
+ last_field = self.packed_fields[-1]
+ offset = last_field.offset + last_field.size
+ pad = GetPad(offset, 8)
+ return offset + pad
+
+
+class ByteInfo(object):
+ def __init__(self):
+ self.is_padding = False
+ self.packed_fields = []
+
+
+def GetByteLayout(packed_struct):
+ bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
+
+ limit_of_previous_field = 0
+ for packed_field in packed_struct.packed_fields:
+ for i in xrange(limit_of_previous_field, packed_field.offset):
+ bytes[i].is_padding = True
+ bytes[packed_field.offset].packed_fields.append(packed_field)
+ limit_of_previous_field = packed_field.offset + packed_field.size
+
+ for i in xrange(limit_of_previous_field, len(bytes)):
+ bytes[i].is_padding = True
+
+ for byte in bytes:
+ # A given byte cannot both be padding and have a fields packed into it.
+ assert not (byte.is_padding and byte.packed_fields)
+
+ return bytes
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
new file mode 100644
index 0000000..a0f664b
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
@@ -0,0 +1,193 @@
+# 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.
+
+import sys
+
+import module as mojom
+import pack
+import test_support
+
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def TestOrdinalOrder():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('testfield1', mojom.INT32, 2)
+ struct.AddField('testfield2', mojom.INT32, 1)
+ ps = pack.PackedStruct(struct)
+
+ errors += EXPECT_EQ(2, len(ps.packed_fields))
+ errors += EXPECT_EQ('testfield2', ps.packed_fields[0].field.name)
+ errors += EXPECT_EQ('testfield1', ps.packed_fields[1].field.name)
+
+ return errors
+
+def TestZeroFields():
+ errors = 0
+ struct = mojom.Struct('test')
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(0, len(ps.packed_fields))
+ return errors
+
+
+def TestOneField():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('testfield1', mojom.INT8)
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(1, len(ps.packed_fields))
+ return errors
+
+# Pass three tuples.
+# |kinds| is a sequence of mojom.Kinds that specify the fields that are to
+# be created.
+# |fields| is the expected order of the resulting fields, with the integer
+# "1" first.
+# |offsets| is the expected order of offsets, with the integer "0" first.
+def TestSequence(kinds, fields, offsets):
+ errors = 0
+ struct = mojom.Struct('test')
+ index = 1
+ for kind in kinds:
+ struct.AddField("%d" % index, kind)
+ index += 1
+ ps = pack.PackedStruct(struct)
+ num_fields = len(ps.packed_fields)
+ errors += EXPECT_EQ(len(kinds), num_fields)
+ for i in xrange(num_fields):
+ EXPECT_EQ("%d" % fields[i], ps.packed_fields[i].field.name)
+ EXPECT_EQ(offsets[i], ps.packed_fields[i].offset)
+
+ return errors
+
+
+def TestPaddingPackedInOrder():
+ return TestSequence(
+ (mojom.INT8, mojom.UINT8, mojom.INT32),
+ (1, 2, 3),
+ (0, 1, 4))
+
+
+def TestPaddingPackedOutOfOrder():
+ return TestSequence(
+ (mojom.INT8, mojom.INT32, mojom.UINT8),
+ (1, 3, 2),
+ (0, 1, 4))
+
+
+def TestPaddingPackedOverflow():
+ kinds = (mojom.INT8, mojom.INT32, mojom.INT16, mojom.INT8, mojom.INT8)
+ # 2 bytes should be packed together first, followed by short, then by int.
+ fields = (1, 4, 3, 2, 5)
+ offsets = (0, 1, 2, 4, 8)
+ return TestSequence(kinds, fields, offsets)
+
+
+def TestNullableTypes():
+ kinds = (mojom.STRING.MakeNullableKind(),
+ mojom.HANDLE.MakeNullableKind(),
+ mojom.Struct('test_struct').MakeNullableKind(),
+ mojom.DCPIPE.MakeNullableKind(),
+ mojom.Array().MakeNullableKind(),
+ mojom.DPPIPE.MakeNullableKind(),
+ mojom.FixedArray(5).MakeNullableKind(),
+ mojom.MSGPIPE.MakeNullableKind(),
+ mojom.Interface('test_inteface').MakeNullableKind(),
+ mojom.SHAREDBUFFER.MakeNullableKind(),
+ mojom.InterfaceRequest().MakeNullableKind())
+ fields = (1, 2, 4, 3, 5, 6, 8, 7, 9, 10, 11)
+ offsets = (0, 8, 12, 16, 24, 32, 36, 40, 48, 52, 56)
+ return TestSequence(kinds, fields, offsets)
+
+
+def TestAllTypes():
+ return TestSequence(
+ (mojom.BOOL, mojom.INT8, mojom.STRING, mojom.UINT8,
+ mojom.INT16, mojom.DOUBLE, mojom.UINT16,
+ mojom.INT32, mojom.UINT32, mojom.INT64,
+ mojom.FLOAT, mojom.STRING, mojom.HANDLE,
+ mojom.UINT64, mojom.Struct('test'), mojom.Array(),
+ mojom.STRING.MakeNullableKind()),
+ (1, 2, 4, 5, 7, 3, 6, 8, 9, 10, 11, 13, 12, 14, 15, 16, 17, 18),
+ (0, 1, 2, 4, 6, 8, 16, 24, 28, 32, 40, 44, 48, 56, 64, 72, 80, 88))
+
+
+def TestPaddingPackedOutOfOrderByOrdinal():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('testfield1', mojom.INT8)
+ struct.AddField('testfield3', mojom.UINT8, 3)
+ struct.AddField('testfield2', mojom.INT32, 2)
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(3, len(ps.packed_fields))
+
+ # Second byte should be packed in behind first, altering order.
+ errors += EXPECT_EQ('testfield1', ps.packed_fields[0].field.name)
+ errors += EXPECT_EQ('testfield3', ps.packed_fields[1].field.name)
+ errors += EXPECT_EQ('testfield2', ps.packed_fields[2].field.name)
+
+ # Second byte should be packed with first.
+ errors += EXPECT_EQ(0, ps.packed_fields[0].offset)
+ errors += EXPECT_EQ(1, ps.packed_fields[1].offset)
+ errors += EXPECT_EQ(4, ps.packed_fields[2].offset)
+
+ return errors
+
+
+def TestBools():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('bit0', mojom.BOOL)
+ struct.AddField('bit1', mojom.BOOL)
+ struct.AddField('int', mojom.INT32)
+ struct.AddField('bit2', mojom.BOOL)
+ struct.AddField('bit3', mojom.BOOL)
+ struct.AddField('bit4', mojom.BOOL)
+ struct.AddField('bit5', mojom.BOOL)
+ struct.AddField('bit6', mojom.BOOL)
+ struct.AddField('bit7', mojom.BOOL)
+ struct.AddField('bit8', mojom.BOOL)
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(10, len(ps.packed_fields))
+
+ # First 8 bits packed together.
+ for i in xrange(8):
+ pf = ps.packed_fields[i]
+ errors += EXPECT_EQ(0, pf.offset)
+ errors += EXPECT_EQ("bit%d" % i, pf.field.name)
+ errors += EXPECT_EQ(i, pf.bit)
+
+ # Ninth bit goes into second byte.
+ errors += EXPECT_EQ("bit8", ps.packed_fields[8].field.name)
+ errors += EXPECT_EQ(1, ps.packed_fields[8].offset)
+ errors += EXPECT_EQ(0, ps.packed_fields[8].bit)
+
+ # int comes last.
+ errors += EXPECT_EQ("int", ps.packed_fields[9].field.name)
+ errors += EXPECT_EQ(4, ps.packed_fields[9].offset)
+
+ return errors
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(TestZeroFields)
+ errors += RunTest(TestOneField)
+ errors += RunTest(TestPaddingPackedInOrder)
+ errors += RunTest(TestPaddingPackedOutOfOrder)
+ errors += RunTest(TestPaddingPackedOverflow)
+ errors += RunTest(TestNullableTypes)
+ errors += RunTest(TestAllTypes)
+ errors += RunTest(TestPaddingPackedOutOfOrderByOrdinal)
+ errors += RunTest(TestBools)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
new file mode 100755
index 0000000..41f11a2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# 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.
+
+""" Test runner for Mojom """
+
+import subprocess
+import sys
+
+def TestMojom(testname, args):
+ print '\nRunning unit tests for %s.' % testname
+ try:
+ args = [sys.executable, testname] + args
+ subprocess.check_call(args, stdout=sys.stdout)
+ print 'Succeeded'
+ return 0
+ except subprocess.CalledProcessError as err:
+ print 'Failed with %s.' % str(err)
+ return 1
+
+
+def main(args):
+ errors = 0
+ errors += TestMojom('data_tests.py', ['--test'])
+ errors += TestMojom('module_tests.py', ['--test'])
+ errors += TestMojom('pack_tests.py', ['--test'])
+
+ if errors:
+ print '\nFailed tests.'
+ return min(errors, 127) # Make sure the return value doesn't "wrap".
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
new file mode 100644
index 0000000..3801d43
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -0,0 +1,59 @@
+# 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.
+
+# Based on:
+# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py
+
+import imp
+import inspect
+import os.path
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("jinja2")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+import jinja2
+
+
+def ApplyTemplate(mojo_generator, base_dir, path_to_template, params,
+ filters=None, **kwargs):
+ template_directory, template_name = os.path.split(path_to_template)
+ path_to_templates = os.path.join(base_dir, template_directory)
+ loader = jinja2.FileSystemLoader([path_to_templates])
+ final_kwargs = dict(mojo_generator.GetJinjaParameters())
+ final_kwargs.update(kwargs)
+ jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True,
+ **final_kwargs)
+ jinja_env.globals.update(mojo_generator.GetGlobals())
+ if filters:
+ jinja_env.filters.update(filters)
+ template = jinja_env.get_template(template_name)
+ return template.render(params)
+
+
+def UseJinja(path_to_template, **kwargs):
+ # Get the directory of our caller's file.
+ base_dir = os.path.dirname(inspect.getfile(sys._getframe(1)))
+ def RealDecorator(generator):
+ def GeneratorInternal(*args, **kwargs2):
+ parameters = generator(*args, **kwargs2)
+ return ApplyTemplate(args[0], base_dir, path_to_template, parameters,
+ **kwargs)
+ GeneratorInternal.func_name = generator.func_name
+ return GeneratorInternal
+ return RealDecorator
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
new file mode 100644
index 0000000..eb39461
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
@@ -0,0 +1,193 @@
+# 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.
+
+import sys
+import traceback
+
+import module as mojom
+
+# Support for writing mojom test cases.
+# RunTest(fn) will execute fn, catching any exceptions. fn should return
+# the number of errors that are encountered.
+#
+# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the
+# expectations are not true and return a non zero value. This allows test cases
+# to be written like this
+#
+# def Foo():
+# errors = 0
+# errors += EXPECT_EQ('test', test())
+# ...
+# return errors
+#
+# RunTest(foo)
+
+def FieldsAreEqual(field1, field2):
+ if field1 == field2:
+ return True
+ return field1.name == field2.name and \
+ KindsAreEqual(field1.kind, field2.kind) and \
+ field1.ordinal == field2.ordinal and \
+ field1.default == field2.default
+
+
+def KindsAreEqual(kind1, kind2):
+ if kind1 == kind2:
+ return True
+ if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec:
+ return False
+ if kind1.__class__ == mojom.Kind:
+ return kind1.spec == kind2.spec
+ if kind1.__class__ == mojom.Struct:
+ if kind1.name != kind2.name or \
+ kind1.spec != kind2.spec or \
+ len(kind1.fields) != len(kind2.fields):
+ return False
+ for i in range(len(kind1.fields)):
+ if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]):
+ return False
+ return True
+ if kind1.__class__ == mojom.Array:
+ return KindsAreEqual(kind1.kind, kind2.kind)
+ print 'Unknown Kind class: ', kind1.__class__.__name__
+ return False
+
+
+def ParametersAreEqual(parameter1, parameter2):
+ if parameter1 == parameter2:
+ return True
+ return parameter1.name == parameter2.name and \
+ parameter1.ordinal == parameter2.ordinal and \
+ parameter1.default == parameter2.default and \
+ KindsAreEqual(parameter1.kind, parameter2.kind)
+
+
+def MethodsAreEqual(method1, method2):
+ if method1 == method2:
+ return True
+ if method1.name != method2.name or \
+ method1.ordinal != method2.ordinal or \
+ len(method1.parameters) != len(method2.parameters):
+ return False
+ for i in range(len(method1.parameters)):
+ if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]):
+ return False
+ return True
+
+
+def InterfacesAreEqual(interface1, interface2):
+ if interface1 == interface2:
+ return True
+ if interface1.name != interface2.name or \
+ len(interface1.methods) != len(interface2.methods):
+ return False
+ for i in range(len(interface1.methods)):
+ if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]):
+ return False
+ return True
+
+
+def ModulesAreEqual(module1, module2):
+ if module1 == module2:
+ return True
+ if module1.name != module2.name or \
+ module1.namespace != module2.namespace or \
+ len(module1.structs) != len(module2.structs) or \
+ len(module1.interfaces) != len(module2.interfaces):
+ return False
+ for i in range(len(module1.structs)):
+ if not KindsAreEqual(module1.structs[i], module2.structs[i]):
+ return False
+ for i in range(len(module1.interfaces)):
+ if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]):
+ return False
+ return True
+
+
+# Builds and returns a Module suitable for testing/
+def BuildTestModule():
+ module = mojom.Module('test', 'testspace')
+ struct = module.AddStruct('teststruct')
+ struct.AddField('testfield1', mojom.INT32)
+ struct.AddField('testfield2', mojom.Array(mojom.INT32), 42)
+
+ interface = module.AddInterface('Server')
+ method = interface.AddMethod('Foo', 42)
+ method.AddParameter('foo', mojom.INT32)
+ method.AddParameter('bar', mojom.Array(struct))
+
+ return module
+
+
+# Tests if |module| is as built by BuildTestModule(). Returns the number of
+# errors
+def TestTestModule(module):
+ errors = 0
+
+ errors += EXPECT_EQ('test', module.name)
+ errors += EXPECT_EQ('testspace', module.namespace)
+ errors += EXPECT_EQ(1, len(module.structs))
+ errors += EXPECT_EQ('teststruct', module.structs[0].name)
+ errors += EXPECT_EQ(2, len(module.structs[0].fields))
+ errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name)
+ errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind)
+ errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name)
+ errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__)
+ errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind)
+
+ errors += EXPECT_EQ(1, len(module.interfaces))
+ errors += EXPECT_EQ('Server', module.interfaces[0].name)
+ errors += EXPECT_EQ(1, len(module.interfaces[0].methods))
+ errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name)
+ errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters))
+ errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name)
+ errors += EXPECT_EQ(mojom.INT32,
+ module.interfaces[0].methods[0].parameters[0].kind)
+ errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name)
+ errors += EXPECT_EQ(
+ mojom.Array,
+ module.interfaces[0].methods[0].parameters[1].kind.__class__)
+ errors += EXPECT_EQ(
+ module.structs[0],
+ module.interfaces[0].methods[0].parameters[1].kind.kind)
+ return errors
+
+
+def PrintFailure(string):
+ stack = traceback.extract_stack()
+ frame = stack[len(stack)-3]
+ sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
+ print "Traceback:"
+ for line in traceback.format_list(stack[:len(stack)-2]):
+ sys.stderr.write(line)
+
+
+def EXPECT_EQ(a, b):
+ if a != b:
+ PrintFailure("%s != %s" % (a, b))
+ return 1
+ return 0
+
+
+def EXPECT_TRUE(a):
+ if not a:
+ PrintFailure('Expecting True')
+ return 1
+ return 0
+
+
+def RunTest(fn):
+ sys.stdout.write('Running %s...' % fn.__name__)
+ try:
+ errors = fn()
+ except:
+ traceback.print_exc(sys.stderr)
+ errors = 1
+ if errors == 0:
+ sys.stdout.write('OK\n')
+ elif errors == 1:
+ sys.stdout.write('1 ERROR\n')
+ else:
+ sys.stdout.write('%d ERRORS\n' % errors)
+ return errors
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
new file mode 100644
index 0000000..1612d98
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -0,0 +1,354 @@
+# 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.
+
+"""Node classes for the AST for a Mojo IDL file."""
+
+# Note: For convenience of testing, you probably want to define __eq__() methods
+# for all node types; it's okay to be slightly lax (e.g., not compare filename
+# and lineno). You may also define __repr__() to help with analyzing test
+# failures, especially for more complex types.
+
+
+class NodeBase(object):
+ """Base class for nodes in the AST."""
+
+ def __init__(self, filename=None, lineno=None):
+ self.filename = filename
+ self.lineno = lineno
+
+ def __eq__(self, other):
+ return type(self) == type(other)
+
+ # Make != the inverse of ==. (Subclasses shouldn't have to override this.)
+ def __ne__(self, other):
+ return not self == other
+
+
+# TODO(vtl): Some of this is complicated enough that it should be tested.
+class NodeListBase(NodeBase):
+ """Represents a list of other nodes, all having the same type. (This is meant
+ to be subclassed, with subclasses defining _list_item_type to be the class (or
+ classes, in a tuple) of the members of the list.)"""
+
+ def __init__(self, item_or_items=None, **kwargs):
+ super(NodeListBase, self).__init__(**kwargs)
+ self.items = []
+ if item_or_items is None:
+ pass
+ elif isinstance(item_or_items, list):
+ for item in item_or_items:
+ assert isinstance(item, self._list_item_type)
+ self.Append(item)
+ else:
+ assert isinstance(item_or_items, self._list_item_type)
+ self.Append(item_or_items)
+
+ # Support iteration. For everything else, users should just access |items|
+ # directly. (We intentionally do NOT supply |__len__()| or |__nonzero__()|, so
+ # |bool(NodeListBase())| is true.)
+ def __iter__(self):
+ return self.items.__iter__()
+
+ def __eq__(self, other):
+ return super(NodeListBase, self).__eq__(other) and \
+ self.items == other.items
+
+ # Implement this so that on failure, we get slightly more sensible output.
+ def __repr__(self):
+ return self.__class__.__name__ + "([" + \
+ ", ".join([repr(elem) for elem in self.items]) + "])"
+
+ def Insert(self, item):
+ """Inserts item at the front of the list."""
+
+ assert isinstance(item, self._list_item_type)
+ self.items.insert(0, item)
+ self._UpdateFilenameAndLineno()
+
+ def Append(self, item):
+ """Appends item to the end of the list."""
+
+ assert isinstance(item, self._list_item_type)
+ self.items.append(item)
+ self._UpdateFilenameAndLineno()
+
+ def _UpdateFilenameAndLineno(self):
+ if self.items:
+ self.filename = self.items[0].filename
+ self.lineno = self.items[0].lineno
+
+
+class Definition(NodeBase):
+ """Represents a definition of anything that has a global name (e.g., enums,
+ enum values, consts, structs, struct fields, interfaces). (This does not
+ include parameter definitions.) This class is meant to be subclassed."""
+
+ def __init__(self, name, **kwargs):
+ assert isinstance(name, str)
+ NodeBase.__init__(self, **kwargs)
+ self.name = name
+
+
+################################################################################
+
+
+class Attribute(NodeBase):
+ """Represents an attribute."""
+
+ def __init__(self, key, value, **kwargs):
+ assert isinstance(key, str)
+ super(Attribute, self).__init__(**kwargs)
+ self.key = key
+ self.value = value
+
+ def __eq__(self, other):
+ return super(Attribute, self).__eq__(other) and \
+ self.key == other.key and \
+ self.value == other.value
+
+
+class AttributeList(NodeListBase):
+ """Represents a list attributes."""
+
+ _list_item_type = Attribute
+
+
+class Const(Definition):
+ """Represents a const definition."""
+
+ def __init__(self, name, typename, value, **kwargs):
+ # The typename is currently passed through as a string.
+ assert isinstance(typename, str)
+ # The value is either a literal (currently passed through as a string) or a
+ # "wrapped identifier".
+ assert isinstance(value, str) or isinstance(value, tuple)
+ super(Const, self).__init__(name, **kwargs)
+ self.typename = typename
+ self.value = value
+
+ def __eq__(self, other):
+ return super(Const, self).__eq__(other) and \
+ self.typename == other.typename and \
+ self.value == other.value
+
+
+class Enum(Definition):
+ """Represents an enum definition."""
+
+ def __init__(self, name, enum_value_list, **kwargs):
+ assert isinstance(enum_value_list, EnumValueList)
+ super(Enum, self).__init__(name, **kwargs)
+ self.enum_value_list = enum_value_list
+
+ def __eq__(self, other):
+ return super(Enum, self).__eq__(other) and \
+ self.enum_value_list == other.enum_value_list
+
+
+class EnumValue(Definition):
+ """Represents a definition of an enum value."""
+
+ def __init__(self, name, value, **kwargs):
+ # The optional value is either an int (which is current a string) or a
+ # "wrapped identifier".
+ assert value is None or isinstance(value, (str, tuple))
+ super(EnumValue, self).__init__(name, **kwargs)
+ self.value = value
+
+ def __eq__(self, other):
+ return super(EnumValue, self).__eq__(other) and \
+ self.value == other.value
+
+
+class EnumValueList(NodeListBase):
+ """Represents a list of enum value definitions (i.e., the "body" of an enum
+ definition)."""
+
+ _list_item_type = EnumValue
+
+
+class Import(NodeBase):
+ """Represents an import statement."""
+
+ def __init__(self, import_filename, **kwargs):
+ assert isinstance(import_filename, str)
+ super(Import, self).__init__(**kwargs)
+ self.import_filename = import_filename
+
+ def __eq__(self, other):
+ return super(Import, self).__eq__(other) and \
+ self.import_filename == other.import_filename
+
+
+class ImportList(NodeListBase):
+ """Represents a list (i.e., sequence) of import statements."""
+
+ _list_item_type = Import
+
+
+class Interface(Definition):
+ """Represents an interface definition."""
+
+ def __init__(self, name, attribute_list, body, **kwargs):
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert isinstance(body, InterfaceBody)
+ super(Interface, self).__init__(name, **kwargs)
+ self.attribute_list = attribute_list
+ self.body = body
+
+ def __eq__(self, other):
+ return super(Interface, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list and \
+ self.body == other.body
+
+
+class Method(Definition):
+ """Represents a method definition."""
+
+ def __init__(self, name, ordinal, parameter_list, response_parameter_list,
+ **kwargs):
+ assert ordinal is None or isinstance(ordinal, Ordinal)
+ assert isinstance(parameter_list, ParameterList)
+ assert response_parameter_list is None or \
+ isinstance(response_parameter_list, ParameterList)
+ super(Method, self).__init__(name, **kwargs)
+ self.ordinal = ordinal
+ self.parameter_list = parameter_list
+ self.response_parameter_list = response_parameter_list
+
+ def __eq__(self, other):
+ return super(Method, self).__eq__(other) and \
+ self.ordinal == other.ordinal and \
+ self.parameter_list == other.parameter_list and \
+ self.response_parameter_list == other.response_parameter_list
+
+
+# This needs to be declared after |Method|.
+class InterfaceBody(NodeListBase):
+ """Represents the body of (i.e., list of definitions inside) an interface."""
+
+ _list_item_type = (Const, Enum, Method)
+
+
+class Module(NodeBase):
+ """Represents a module statement."""
+
+ def __init__(self, name, attribute_list, **kwargs):
+ # |name| is either none or a "wrapped identifier".
+ assert name is None or isinstance(name, tuple)
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ super(Module, self).__init__(**kwargs)
+ self.name = name
+ self.attribute_list = attribute_list
+
+ def __eq__(self, other):
+ return super(Module, self).__eq__(other) and \
+ self.name == other.name and \
+ self.attribute_list == other.attribute_list
+
+
+class Mojom(NodeBase):
+ """Represents an entire .mojom file. (This is the root node."""
+
+ def __init__(self, module, import_list, definition_list, **kwargs):
+ assert module is None or isinstance(module, Module)
+ assert isinstance(import_list, ImportList)
+ assert isinstance(definition_list, list)
+ super(Mojom, self).__init__(**kwargs)
+ self.module = module
+ self.import_list = import_list
+ self.definition_list = definition_list
+
+ def __eq__(self, other):
+ return super(Mojom, self).__eq__(other) and \
+ self.module == other.module and \
+ self.import_list == other.import_list and \
+ self.definition_list == other.definition_list
+
+ def __repr__(self):
+ return "%s(%r, %r, %r)" % (self.__class__.__name__, self.module,
+ self.import_list, self.definition_list)
+
+
+class Ordinal(NodeBase):
+ """Represents an ordinal value labeling, e.g., a struct field."""
+
+ def __init__(self, value, **kwargs):
+ assert isinstance(value, int)
+ super(Ordinal, self).__init__(**kwargs)
+ self.value = value
+
+ def __eq__(self, other):
+ return super(Ordinal, self).__eq__(other) and \
+ self.value == other.value
+
+
+class Parameter(NodeBase):
+ """Represents a method request or response parameter."""
+
+ def __init__(self, name, ordinal, typename, **kwargs):
+ assert isinstance(name, str)
+ assert ordinal is None or isinstance(ordinal, Ordinal)
+ assert isinstance(typename, str)
+ super(Parameter, self).__init__(**kwargs)
+ self.name = name
+ self.ordinal = ordinal
+ self.typename = typename
+
+ def __eq__(self, other):
+ return super(Parameter, self).__eq__(other) and \
+ self.name == other.name and \
+ self.ordinal == other.ordinal and \
+ self.typename == other.typename
+
+
+class ParameterList(NodeListBase):
+ """Represents a list of (method request or response) parameters."""
+
+ _list_item_type = Parameter
+
+
+class Struct(Definition):
+ """Represents a struct definition."""
+
+ def __init__(self, name, attribute_list, body, **kwargs):
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert isinstance(body, StructBody)
+ super(Struct, self).__init__(name, **kwargs)
+ self.attribute_list = attribute_list
+ self.body = body
+
+ def __eq__(self, other):
+ return super(Struct, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list and \
+ self.body == other.body
+
+
+class StructField(Definition):
+ """Represents a struct field definition."""
+
+ def __init__(self, name, ordinal, typename, default_value, **kwargs):
+ assert isinstance(name, str)
+ assert ordinal is None or isinstance(ordinal, Ordinal)
+ assert isinstance(typename, str)
+ # The optional default value is currently either a value as a string or a
+ # "wrapped identifier".
+ assert default_value is None or isinstance(default_value, (str, tuple))
+ super(StructField, self).__init__(name, **kwargs)
+ self.ordinal = ordinal
+ self.typename = typename
+ self.default_value = default_value
+
+ def __eq__(self, other):
+ return super(StructField, self).__eq__(other) and \
+ self.ordinal == other.ordinal and \
+ self.typename == other.typename and \
+ self.default_value == other.default_value
+
+
+# This needs to be declared after |StructField|.
+class StructBody(NodeListBase):
+ """Represents the body of (i.e., list of definitions inside) a struct."""
+
+ _list_item_type = (Const, Enum, StructField)
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
new file mode 100644
index 0000000..b13fac3
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
@@ -0,0 +1,251 @@
+# 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 imp
+import os.path
+import sys
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply.lex import TOKEN
+
+from ..error import Error
+
+
+class LexError(Error):
+ """Class for errors from the lexer."""
+
+ def __init__(self, filename, message, lineno):
+ Error.__init__(self, filename, message, lineno=lineno)
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Lexer(object):
+
+ def __init__(self, filename):
+ self.filename = filename
+
+ ######################-- PRIVATE --######################
+
+ ##
+ ## Internal auxiliary methods
+ ##
+ def _error(self, msg, token):
+ raise LexError(self.filename, msg, token.lineno)
+
+ ##
+ ## Reserved keywords
+ ##
+ keywords = (
+ 'HANDLE',
+
+ 'IMPORT',
+ 'MODULE',
+ 'STRUCT',
+ 'INTERFACE',
+ 'ENUM',
+ 'CONST',
+ 'TRUE',
+ 'FALSE',
+ 'DEFAULT',
+ 'ARRAY'
+ )
+
+ keyword_map = {}
+ for keyword in keywords:
+ keyword_map[keyword.lower()] = keyword
+
+ ##
+ ## All the tokens recognized by the lexer
+ ##
+ tokens = keywords + (
+ # Identifiers
+ 'NAME',
+
+ # Constants
+ 'ORDINAL',
+ 'INT_CONST_DEC', 'INT_CONST_HEX',
+ 'FLOAT_CONST',
+
+ # String literals
+ 'STRING_LITERAL',
+
+ # Operators
+ 'MINUS',
+ 'PLUS',
+ 'AMP',
+ 'QSTN',
+
+ # Assignment
+ 'EQUALS',
+
+ # Request / response
+ 'RESPONSE',
+
+ # Delimiters
+ 'LPAREN', 'RPAREN', # ( )
+ 'LBRACKET', 'RBRACKET', # [ ]
+ 'LBRACE', 'RBRACE', # { }
+ 'LANGLE', 'RANGLE', # < >
+ 'SEMI', # ;
+ 'COMMA', 'DOT' # , .
+ )
+
+ ##
+ ## Regexes for use in tokens
+ ##
+
+ # valid C identifiers (K&R2: A.2.3)
+ identifier = r'[a-zA-Z_][0-9a-zA-Z_]*'
+
+ hex_prefix = '0[xX]'
+ hex_digits = '[0-9a-fA-F]+'
+
+ # integer constants (K&R2: A.2.5.1)
+ decimal_constant = '0|([1-9][0-9]*)'
+ hex_constant = hex_prefix+hex_digits
+ # Don't allow octal constants (even invalid octal).
+ octal_constant_disallowed = '0[0-9]+'
+
+ # character constants (K&R2: A.2.5.2)
+ # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
+ # directives with Windows paths as filenames (..\..\dir\file)
+ # For the same reason, decimal_escape allows all digit sequences. We want to
+ # parse all correct code, even if it means to sometimes parse incorrect
+ # code.
+ #
+ simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
+ decimal_escape = r"""(\d+)"""
+ hex_escape = r"""(x[0-9a-fA-F]+)"""
+ bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
+
+ escape_sequence = \
+ r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))'
+
+ # string literals (K&R2: A.2.6)
+ string_char = r"""([^"\\\n]|"""+escape_sequence+')'
+ string_literal = '"'+string_char+'*"'
+ bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"'
+
+ # floating constants (K&R2: A.2.5.3)
+ exponent_part = r"""([eE][-+]?[0-9]+)"""
+ fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)"""
+ floating_constant = \
+ '(((('+fractional_constant+')'+ \
+ exponent_part+'?)|([0-9]+'+exponent_part+')))'
+
+ # Ordinals
+ ordinal = r'@[0-9]+'
+ missing_ordinal_value = r'@'
+ # Don't allow ordinal values in octal (even invalid octal, like 09) or
+ # hexadecimal.
+ octal_or_hex_ordinal_disallowed = r'@((0[0-9]+)|('+hex_prefix+hex_digits+'))'
+
+ ##
+ ## Rules for the normal state
+ ##
+ t_ignore = ' \t\r'
+
+ # Newlines
+ def t_NEWLINE(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+
+ # Operators
+ t_MINUS = r'-'
+ t_PLUS = r'\+'
+ t_AMP = r'&'
+ t_QSTN = r'\?'
+
+ # =
+ t_EQUALS = r'='
+
+ # =>
+ t_RESPONSE = r'=>'
+
+ # Delimiters
+ t_LPAREN = r'\('
+ t_RPAREN = r'\)'
+ t_LBRACKET = r'\['
+ t_RBRACKET = r'\]'
+ t_LBRACE = r'\{'
+ t_RBRACE = r'\}'
+ t_LANGLE = r'<'
+ t_RANGLE = r'>'
+ t_COMMA = r','
+ t_DOT = r'\.'
+ t_SEMI = r';'
+
+ t_STRING_LITERAL = string_literal
+
+ # The following floating and integer constants are defined as
+ # functions to impose a strict order (otherwise, decimal
+ # is placed before the others because its regex is longer,
+ # and this is bad)
+ #
+ @TOKEN(floating_constant)
+ def t_FLOAT_CONST(self, t):
+ return t
+
+ @TOKEN(hex_constant)
+ def t_INT_CONST_HEX(self, t):
+ return t
+
+ @TOKEN(octal_constant_disallowed)
+ def t_OCTAL_CONSTANT_DISALLOWED(self, t):
+ msg = "Octal values not allowed"
+ self._error(msg, t)
+
+ @TOKEN(decimal_constant)
+ def t_INT_CONST_DEC(self, t):
+ return t
+
+ # unmatched string literals are caught by the preprocessor
+
+ @TOKEN(bad_string_literal)
+ def t_BAD_STRING_LITERAL(self, t):
+ msg = "String contains invalid escape code"
+ self._error(msg, t)
+
+ # Handle ordinal-related tokens in the right order:
+ @TOKEN(octal_or_hex_ordinal_disallowed)
+ def t_OCTAL_OR_HEX_ORDINAL_DISALLOWED(self, t):
+ msg = "Octal and hexadecimal ordinal values not allowed"
+ self._error(msg, t)
+
+ @TOKEN(ordinal)
+ def t_ORDINAL(self, t):
+ return t
+
+ @TOKEN(missing_ordinal_value)
+ def t_BAD_ORDINAL(self, t):
+ msg = "Missing ordinal value"
+ self._error(msg, t)
+
+ @TOKEN(identifier)
+ def t_NAME(self, t):
+ t.type = self.keyword_map.get(t.value, "NAME")
+ return t
+
+ # Ignore C and C++ style comments
+ def t_COMMENT(self, t):
+ r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)'
+ t.lexer.lineno += t.value.count("\n")
+
+ def t_error(self, t):
+ msg = "Illegal character %s" % repr(t.value[0])
+ self._error(msg, t)
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
new file mode 100644
index 0000000..551c049
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -0,0 +1,383 @@
+# 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.
+
+"""Generates a syntax tree from a Mojo IDL file."""
+
+import imp
+import os.path
+import sys
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+from ply import yacc
+
+from ..error import Error
+from . import ast
+from .lexer import Lexer
+
+
+_MAX_ORDINAL_VALUE = 0xffffffff
+_MAX_ARRAY_SIZE = 0xffffffff
+
+
+class ParseError(Error):
+ """Class for errors from the parser."""
+
+ def __init__(self, filename, message, lineno=None, snippet=None):
+ Error.__init__(self, filename, message, lineno=lineno,
+ addenda=([snippet] if snippet else None))
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Parser(object):
+
+ def __init__(self, lexer, source, filename):
+ self.tokens = lexer.tokens
+ self.source = source
+ self.filename = filename
+
+ # Names of functions
+ #
+ # In general, we name functions after the left-hand-side of the rule(s) that
+ # they handle. E.g., |p_foo_bar| for a rule |foo_bar : ...|.
+ #
+ # There may be multiple functions handling rules for the same left-hand-side;
+ # then we name the functions |p_foo_bar_N| (for left-hand-side |foo_bar|),
+ # where N is a number (numbered starting from 1). Note that using multiple
+ # functions is actually more efficient than having single functions handle
+ # multiple rules (and, e.g., distinguishing them by examining |len(p)|).
+ #
+ # It's also possible to have a function handling multiple rules with different
+ # left-hand-sides. We do not do this.
+ #
+ # See http://www.dabeaz.com/ply/ply.html#ply_nn25 for more details.
+
+ # TODO(vtl): Get rid of the braces in the module "statement". (Consider
+ # renaming "module" -> "package".) Then we'll be able to have a single rule
+ # for root (by making module "optional").
+ def p_root_1(self, p):
+ """root : import_list module LBRACE definition_list RBRACE"""
+ p[0] = ast.Mojom(p[2], p[1], p[4])
+
+ def p_root_2(self, p):
+ """root : import_list definition_list"""
+ p[0] = ast.Mojom(None, p[1], p[2])
+
+ def p_import_list_1(self, p):
+ """import_list : """
+ p[0] = ast.ImportList()
+
+ def p_import_list_2(self, p):
+ """import_list : import_list import"""
+ p[0] = p[1]
+ p[0].Append(p[2])
+
+ def p_import(self, p):
+ """import : IMPORT STRING_LITERAL"""
+ # 'eval' the literal to strip the quotes.
+ # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves.
+ p[0] = ast.Import(eval(p[2]))
+
+ def p_module(self, p):
+ """module : attribute_section MODULE identifier_wrapped """
+ p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2))
+
+ def p_definition_list(self, p):
+ """definition_list : definition definition_list
+ | """
+ if len(p) > 1:
+ p[0] = p[2]
+ p[0].insert(0, p[1])
+ else:
+ p[0] = []
+
+ def p_definition(self, p):
+ """definition : struct
+ | interface
+ | enum
+ | const"""
+ p[0] = p[1]
+
+ def p_attribute_section_1(self, p):
+ """attribute_section : """
+ p[0] = None
+
+ def p_attribute_section_2(self, p):
+ """attribute_section : LBRACKET attribute_list RBRACKET"""
+ p[0] = p[2]
+
+ def p_attribute_list_1(self, p):
+ """attribute_list : """
+ p[0] = ast.AttributeList()
+
+ def p_attribute_list_2(self, p):
+ """attribute_list : nonempty_attribute_list"""
+ p[0] = p[1]
+
+ def p_nonempty_attribute_list_1(self, p):
+ """nonempty_attribute_list : attribute"""
+ p[0] = ast.AttributeList(p[1])
+
+ def p_nonempty_attribute_list_2(self, p):
+ """nonempty_attribute_list : nonempty_attribute_list COMMA attribute"""
+ p[0] = p[1]
+ p[0].Append(p[3])
+
+ def p_attribute(self, p):
+ """attribute : NAME EQUALS evaled_literal
+ | NAME EQUALS NAME"""
+ p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))
+
+ def p_evaled_literal(self, p):
+ """evaled_literal : literal"""
+ # 'eval' the literal to strip the quotes.
+ p[0] = eval(p[1])
+
+ def p_struct(self, p):
+ """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
+ p[0] = ast.Struct(p[3], p[1], p[5])
+
+ def p_struct_body_1(self, p):
+ """struct_body : """
+ p[0] = ast.StructBody()
+
+ def p_struct_body_2(self, p):
+ """struct_body : struct_body const
+ | struct_body enum
+ | struct_body struct_field"""
+ p[0] = p[1]
+ p[0].Append(p[2])
+
+ def p_struct_field(self, p):
+ """struct_field : typename NAME ordinal default SEMI"""
+ p[0] = ast.StructField(p[2], p[3], p[1], p[4])
+
+ def p_default_1(self, p):
+ """default : """
+ p[0] = None
+
+ def p_default_2(self, p):
+ """default : EQUALS constant"""
+ p[0] = p[2]
+
+ def p_interface(self, p):
+ """interface : attribute_section INTERFACE NAME LBRACE interface_body \
+ RBRACE SEMI"""
+ p[0] = ast.Interface(p[3], p[1], p[5])
+
+ def p_interface_body_1(self, p):
+ """interface_body : """
+ p[0] = ast.InterfaceBody()
+
+ def p_interface_body_2(self, p):
+ """interface_body : interface_body const
+ | interface_body enum
+ | interface_body method"""
+ p[0] = p[1]
+ p[0].Append(p[2])
+
+ def p_response_1(self, p):
+ """response : """
+ p[0] = None
+
+ def p_response_2(self, p):
+ """response : RESPONSE LPAREN parameter_list RPAREN"""
+ p[0] = p[3]
+
+ def p_method(self, p):
+ """method : NAME ordinal LPAREN parameter_list RPAREN response SEMI"""
+ p[0] = ast.Method(p[1], p[2], p[4], p[6])
+
+ def p_parameter_list_1(self, p):
+ """parameter_list : """
+ p[0] = ast.ParameterList()
+
+ def p_parameter_list_2(self, p):
+ """parameter_list : nonempty_parameter_list"""
+ p[0] = p[1]
+
+ def p_nonempty_parameter_list_1(self, p):
+ """nonempty_parameter_list : parameter"""
+ p[0] = ast.ParameterList(p[1])
+
+ def p_nonempty_parameter_list_2(self, p):
+ """nonempty_parameter_list : nonempty_parameter_list COMMA parameter"""
+ p[0] = p[1]
+ p[0].Append(p[3])
+
+ def p_parameter(self, p):
+ """parameter : typename NAME ordinal"""
+ p[0] = ast.Parameter(p[2], p[3], p[1],
+ filename=self.filename, lineno=p.lineno(2))
+
+ def p_typename(self, p):
+ """typename : nonnullable_typename QSTN
+ | nonnullable_typename"""
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ p[0] = p[1] + "?"
+
+ def p_nonnullable_typename(self, p):
+ """nonnullable_typename : basictypename
+ | array
+ | fixed_array
+ | interfacerequest"""
+ p[0] = p[1]
+
+ def p_basictypename(self, p):
+ """basictypename : identifier
+ | handletype"""
+ p[0] = p[1]
+
+ def p_handletype(self, p):
+ """handletype : HANDLE
+ | HANDLE LANGLE NAME RANGLE"""
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ if p[3] not in ('data_pipe_consumer',
+ 'data_pipe_producer',
+ 'message_pipe',
+ 'shared_buffer'):
+ # Note: We don't enable tracking of line numbers for everything, so we
+ # can't use |p.lineno(3)|.
+ raise ParseError(self.filename, "Invalid handle type %r:" % p[3],
+ lineno=p.lineno(1),
+ snippet=self._GetSnippet(p.lineno(1)))
+ p[0] = "handle<" + p[3] + ">"
+
+ def p_array(self, p):
+ """array : ARRAY LANGLE typename RANGLE"""
+ p[0] = p[3] + "[]"
+
+ def p_fixed_array(self, p):
+ """fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE"""
+ value = int(p[5])
+ if value == 0 or value > _MAX_ARRAY_SIZE:
+ raise ParseError(self.filename, "Fixed array size %d invalid" % value,
+ lineno=p.lineno(5),
+ snippet=self._GetSnippet(p.lineno(5)))
+ p[0] = p[3] + "[" + p[5] + "]"
+
+ def p_interfacerequest(self, p):
+ """interfacerequest : identifier AMP"""
+ p[0] = p[1] + "&"
+
+ def p_ordinal_1(self, p):
+ """ordinal : """
+ p[0] = None
+
+ def p_ordinal_2(self, p):
+ """ordinal : ORDINAL"""
+ value = int(p[1][1:])
+ if value > _MAX_ORDINAL_VALUE:
+ raise ParseError(self.filename, "Ordinal value %d too large:" % value,
+ lineno=p.lineno(1),
+ snippet=self._GetSnippet(p.lineno(1)))
+ p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
+
+ def p_enum(self, p):
+ """enum : ENUM NAME LBRACE nonempty_enum_value_list RBRACE SEMI
+ | ENUM NAME LBRACE nonempty_enum_value_list COMMA RBRACE SEMI"""
+ p[0] = ast.Enum(p[2], p[4], filename=self.filename, lineno=p.lineno(1))
+
+ def p_nonempty_enum_value_list_1(self, p):
+ """nonempty_enum_value_list : enum_value"""
+ p[0] = ast.EnumValueList(p[1])
+
+ def p_nonempty_enum_value_list_2(self, p):
+ """nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value"""
+ p[0] = p[1]
+ p[0].Append(p[3])
+
+ def p_enum_value(self, p):
+ """enum_value : NAME
+ | NAME EQUALS int
+ | NAME EQUALS identifier_wrapped"""
+ p[0] = ast.EnumValue(p[1], p[3] if len(p) == 4 else None,
+ filename=self.filename, lineno=p.lineno(1))
+
+ def p_const(self, p):
+ """const : CONST typename NAME EQUALS constant SEMI"""
+ p[0] = ast.Const(p[3], p[2], p[5])
+
+ def p_constant(self, p):
+ """constant : literal
+ | identifier_wrapped"""
+ p[0] = p[1]
+
+ def p_identifier_wrapped(self, p):
+ """identifier_wrapped : identifier"""
+ p[0] = ('IDENTIFIER', p[1])
+
+ # TODO(vtl): Make this produce a "wrapped" identifier (probably as an
+ # |ast.Identifier|, to be added) and get rid of identifier_wrapped.
+ def p_identifier(self, p):
+ """identifier : NAME
+ | NAME DOT identifier"""
+ p[0] = ''.join(p[1:])
+
+ def p_literal(self, p):
+ """literal : int
+ | float
+ | TRUE
+ | FALSE
+ | DEFAULT
+ | STRING_LITERAL"""
+ p[0] = p[1]
+
+ def p_int(self, p):
+ """int : int_const
+ | PLUS int_const
+ | MINUS int_const"""
+ p[0] = ''.join(p[1:])
+
+ def p_int_const(self, p):
+ """int_const : INT_CONST_DEC
+ | INT_CONST_HEX"""
+ p[0] = p[1]
+
+ def p_float(self, p):
+ """float : FLOAT_CONST
+ | PLUS FLOAT_CONST
+ | MINUS FLOAT_CONST"""
+ p[0] = ''.join(p[1:])
+
+ def p_error(self, e):
+ if e is None:
+ # Unexpected EOF.
+ # TODO(vtl): Can we figure out what's missing?
+ raise ParseError(self.filename, "Unexpected end of file")
+
+ raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno,
+ snippet=self._GetSnippet(e.lineno))
+
+ def _GetSnippet(self, lineno):
+ return self.source.split('\n')[lineno - 1]
+
+
+def Parse(source, filename):
+ lexer = Lexer(filename)
+ parser = Parser(lexer, source, filename)
+
+ lex.lex(object=lexer)
+ yacc.yacc(module=parser, debug=0, write_tables=0)
+
+ tree = yacc.parse(source)
+ return tree
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
new file mode 100644
index 0000000..77e92c5
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
@@ -0,0 +1,160 @@
+# 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.
+
+"""Translates parse tree to Mojom IR."""
+
+
+import re
+
+from . import ast
+
+
+def _MapTreeForType(func, tree, type_to_map):
+ assert isinstance(type_to_map, type)
+ if not tree:
+ return []
+ return [func(subtree) for subtree in tree if isinstance(subtree, type_to_map)]
+
+_FIXED_ARRAY_REGEXP = re.compile(r'\[[0-9]+\]')
+
+def _MapKind(kind):
+ map_to_kind = {'bool': 'b',
+ 'int8': 'i8',
+ 'int16': 'i16',
+ 'int32': 'i32',
+ 'int64': 'i64',
+ 'uint8': 'u8',
+ 'uint16': 'u16',
+ 'uint32': 'u32',
+ 'uint64': 'u64',
+ 'float': 'f',
+ 'double': 'd',
+ 'string': 's',
+ 'handle': 'h',
+ 'handle<data_pipe_consumer>': 'h:d:c',
+ 'handle<data_pipe_producer>': 'h:d:p',
+ 'handle<message_pipe>': 'h:m',
+ 'handle<shared_buffer>': 'h:s'}
+ if kind.endswith('?'):
+ base_kind = _MapKind(kind[0:-1])
+ # NOTE: This doesn't rule out enum types. Those will be detected later, when
+ # cross-reference is established.
+ reference_kinds = ('s', 'h', 'a', 'r', 'x')
+ if base_kind[0] not in reference_kinds:
+ raise Exception(
+ 'A type (spec "%s") cannot be made nullable' % base_kind)
+ return '?' + base_kind
+ if kind.endswith('[]'):
+ typename = kind[0:-2]
+ if _FIXED_ARRAY_REGEXP.search(typename):
+ raise Exception('Arrays of fixed sized arrays not supported')
+ return 'a:' + _MapKind(typename)
+ if kind.endswith(']'):
+ lbracket = kind.rfind('[')
+ typename = kind[0:lbracket]
+ if typename.find('[') != -1:
+ raise Exception('Fixed sized arrays of arrays not supported')
+ return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
+ if kind.endswith('&'):
+ return 'r:' + _MapKind(kind[0:-1])
+ if kind in map_to_kind:
+ return map_to_kind[kind]
+ return 'x:' + kind
+
+def _AttributeListToDict(attribute_list):
+ if attribute_list is None:
+ return {}
+ assert isinstance(attribute_list, ast.AttributeList)
+ # TODO(vtl): Check for duplicate keys here.
+ return dict([(attribute.key, attribute.value)
+ for attribute in attribute_list])
+
+def _EnumToDict(enum):
+ def EnumValueToDict(enum_value):
+ assert isinstance(enum_value, ast.EnumValue)
+ return {'name': enum_value.name,
+ 'value': enum_value.value}
+
+ assert isinstance(enum, ast.Enum)
+ return {'name': enum.name,
+ 'fields': map(EnumValueToDict, enum.enum_value_list)}
+
+def _ConstToDict(const):
+ assert isinstance(const, ast.Const)
+ return {'name': const.name,
+ 'kind': _MapKind(const.typename),
+ 'value': const.value}
+
+
+class _MojomBuilder(object):
+ def __init__(self):
+ self.mojom = {}
+
+ def Build(self, tree, name):
+ def StructToDict(struct):
+ def StructFieldToDict(struct_field):
+ assert isinstance(struct_field, ast.StructField)
+ return {'name': struct_field.name,
+ 'kind': _MapKind(struct_field.typename),
+ 'ordinal': struct_field.ordinal.value \
+ if struct_field.ordinal else None,
+ 'default': struct_field.default_value}
+
+ assert isinstance(struct, ast.Struct)
+ return {'name': struct.name,
+ 'attributes': _AttributeListToDict(struct.attribute_list),
+ 'fields': _MapTreeForType(StructFieldToDict, struct.body,
+ ast.StructField),
+ 'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum),
+ 'constants': _MapTreeForType(_ConstToDict, struct.body,
+ ast.Const)}
+
+ def InterfaceToDict(interface):
+ def MethodToDict(method):
+ def ParameterToDict(param):
+ assert isinstance(param, ast.Parameter)
+ return {'name': param.name,
+ 'kind': _MapKind(param.typename),
+ 'ordinal': param.ordinal.value if param.ordinal else None}
+
+ assert isinstance(method, ast.Method)
+ rv = {'name': method.name,
+ 'parameters': map(ParameterToDict, method.parameter_list),
+ 'ordinal': method.ordinal.value if method.ordinal else None}
+ if method.response_parameter_list is not None:
+ rv['response_parameters'] = map(ParameterToDict,
+ method.response_parameter_list)
+ return rv
+
+ assert isinstance(interface, ast.Interface)
+ attributes = _AttributeListToDict(interface.attribute_list)
+ return {'name': interface.name,
+ 'attributes': attributes,
+ 'client': attributes.get('Client'),
+ 'methods': _MapTreeForType(MethodToDict, interface.body,
+ ast.Method),
+ 'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum),
+ 'constants': _MapTreeForType(_ConstToDict, interface.body,
+ ast.Const)}
+
+ assert isinstance(tree, ast.Mojom)
+ self.mojom['name'] = name
+ self.mojom['namespace'] = tree.module.name[1] if tree.module else ''
+ self.mojom['imports'] = \
+ [{'filename': imp.import_filename} for imp in tree.import_list]
+ self.mojom['attributes'] = \
+ _AttributeListToDict(tree.module.attribute_list) if tree.module else {}
+ self.mojom['structs'] = \
+ _MapTreeForType(StructToDict, tree.definition_list, ast.Struct)
+ self.mojom['interfaces'] = \
+ _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface)
+ self.mojom['enums'] = \
+ _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum)
+ self.mojom['constants'] = \
+ _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const)
+ return self.mojom
+
+
+def Translate(tree, name):
+ return _MojomBuilder().Build(tree, name)
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py
new file mode 100644
index 0000000..dd28cdd
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py
@@ -0,0 +1,135 @@
+# 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 imp
+import os.path
+import sys
+import unittest
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("mojom")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.ast as ast
+
+
+class _TestNode(ast.NodeBase):
+ """Node type for tests."""
+
+ def __init__(self, value, **kwargs):
+ super(_TestNode, self).__init__(**kwargs)
+ self.value = value
+
+ def __eq__(self, other):
+ return super(_TestNode, self).__eq__(other) and self.value == other.value
+
+
+class _TestNodeList(ast.NodeListBase):
+ """Node list type for tests."""
+
+ _list_item_type = _TestNode
+
+
+class ASTTest(unittest.TestCase):
+ """Tests various AST classes."""
+
+ def testNodeBase(self):
+ # Test |__eq__()|; this is only used for testing, where we want to do
+ # comparison by value and ignore filenames/line numbers (for convenience).
+ node1 = ast.NodeBase(filename="hello.mojom", lineno=123)
+ node2 = ast.NodeBase()
+ self.assertEquals(node1, node2)
+ self.assertEquals(node2, node1)
+
+ # Check that |__ne__()| just defers to |__eq__()| properly.
+ self.assertFalse(node1 != node2)
+ self.assertFalse(node2 != node1)
+
+ # Check that |filename| and |lineno| are set properly (and are None by
+ # default).
+ self.assertEquals(node1.filename, "hello.mojom")
+ self.assertEquals(node1.lineno, 123)
+ self.assertIsNone(node2.filename)
+ self.assertIsNone(node2.lineno)
+
+ # |NodeBase|'s |__eq__()| should compare types (and a subclass's |__eq__()|
+ # should first defer to its superclass's).
+ node3 = _TestNode(123)
+ self.assertNotEqual(node1, node3)
+ self.assertNotEqual(node3, node1)
+ # Also test |__eq__()| directly.
+ self.assertFalse(node1 == node3)
+ self.assertFalse(node3 == node1)
+
+ node4 = _TestNode(123, filename="world.mojom", lineno=123)
+ self.assertEquals(node4, node3)
+ node5 = _TestNode(456)
+ self.assertNotEquals(node5, node4)
+
+ def testNodeListBase(self):
+ node1 = _TestNode(1, filename="foo.mojom", lineno=1)
+ # Equal to, but not the same as, |node1|:
+ node1b = _TestNode(1, filename="foo.mojom", lineno=1)
+ node2 = _TestNode(2, filename="foo.mojom", lineno=2)
+
+ nodelist1 = _TestNodeList() # Contains: (empty).
+ self.assertEquals(nodelist1, nodelist1)
+ self.assertEquals(nodelist1.items, [])
+ self.assertIsNone(nodelist1.filename)
+ self.assertIsNone(nodelist1.lineno)
+
+ nodelist2 = _TestNodeList(node1) # Contains: 1.
+ self.assertEquals(nodelist2, nodelist2)
+ self.assertEquals(nodelist2.items, [node1])
+ self.assertNotEqual(nodelist2, nodelist1)
+ self.assertEquals(nodelist2.filename, "foo.mojom")
+ self.assertEquals(nodelist2.lineno, 1)
+
+ nodelist3 = _TestNodeList([node2]) # Contains: 2.
+ self.assertEquals(nodelist3.items, [node2])
+ self.assertNotEqual(nodelist3, nodelist1)
+ self.assertNotEqual(nodelist3, nodelist2)
+ self.assertEquals(nodelist3.filename, "foo.mojom")
+ self.assertEquals(nodelist3.lineno, 2)
+
+ nodelist1.Append(node1b) # Contains: 1.
+ self.assertEquals(nodelist1.items, [node1])
+ self.assertEquals(nodelist1, nodelist2)
+ self.assertNotEqual(nodelist1, nodelist3)
+ self.assertEquals(nodelist1.filename, "foo.mojom")
+ self.assertEquals(nodelist1.lineno, 1)
+
+ nodelist1.Append(node2) # Contains: 1, 2.
+ self.assertEquals(nodelist1.items, [node1, node2])
+ self.assertNotEqual(nodelist1, nodelist2)
+ self.assertNotEqual(nodelist1, nodelist3)
+ self.assertEquals(nodelist1.lineno, 1)
+
+ nodelist2.Append(node2) # Contains: 1, 2.
+ self.assertEquals(nodelist2.items, [node1, node2])
+ self.assertEquals(nodelist2, nodelist1)
+ self.assertNotEqual(nodelist2, nodelist3)
+ self.assertEquals(nodelist2.lineno, 1)
+
+ nodelist3.Insert(node1) # Contains: 1, 2.
+ self.assertEquals(nodelist3.items, [node1, node2])
+ self.assertEquals(nodelist3, nodelist1)
+ self.assertEquals(nodelist3, nodelist2)
+ self.assertEquals(nodelist3.lineno, 1)
+
+ # Test iteration:
+ i = 1
+ for item in nodelist1:
+ self.assertEquals(item.value, i)
+ i += 1
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
new file mode 100644
index 0000000..d24b7e2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
@@ -0,0 +1,186 @@
+# 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 imp
+import os.path
+import sys
+import unittest
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+
+try:
+ imp.find_module("mojom")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.lexer
+
+
+# This (monkey-patching LexToken to make comparison value-based) is evil, but
+# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
+# for object identity.)
+def _LexTokenEq(self, other):
+ return self.type == other.type and self.value == other.value and \
+ self.lineno == other.lineno and self.lexpos == other.lexpos
+setattr(lex.LexToken, '__eq__', _LexTokenEq)
+
+
+def _MakeLexToken(token_type, value, lineno=1, lexpos=0):
+ """Makes a LexToken with the given parameters. (Note that lineno is 1-based,
+ but lexpos is 0-based.)"""
+ rv = lex.LexToken()
+ rv.type, rv.value, rv.lineno, rv.lexpos = token_type, value, lineno, lexpos
+ return rv
+
+
+def _MakeLexTokenForKeyword(keyword, **kwargs):
+ """Makes a LexToken for the given keyword."""
+ return _MakeLexToken(keyword.upper(), keyword.lower(), **kwargs)
+
+
+class LexerTest(unittest.TestCase):
+ """Tests |mojom.parse.lexer.Lexer|."""
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ # Clone all lexer instances from this one, since making a lexer is slow.
+ self._zygote_lexer = lex.lex(mojom.parse.lexer.Lexer("my_file.mojom"))
+
+ def testValidKeywords(self):
+ """Tests valid keywords."""
+ self.assertEquals(self._SingleTokenForInput("handle"),
+ _MakeLexTokenForKeyword("handle"))
+ self.assertEquals(self._SingleTokenForInput("import"),
+ _MakeLexTokenForKeyword("import"))
+ self.assertEquals(self._SingleTokenForInput("module"),
+ _MakeLexTokenForKeyword("module"))
+ self.assertEquals(self._SingleTokenForInput("struct"),
+ _MakeLexTokenForKeyword("struct"))
+ self.assertEquals(self._SingleTokenForInput("interface"),
+ _MakeLexTokenForKeyword("interface"))
+ self.assertEquals(self._SingleTokenForInput("enum"),
+ _MakeLexTokenForKeyword("enum"))
+ self.assertEquals(self._SingleTokenForInput("const"),
+ _MakeLexTokenForKeyword("const"))
+ self.assertEquals(self._SingleTokenForInput("true"),
+ _MakeLexTokenForKeyword("true"))
+ self.assertEquals(self._SingleTokenForInput("false"),
+ _MakeLexTokenForKeyword("false"))
+ self.assertEquals(self._SingleTokenForInput("default"),
+ _MakeLexTokenForKeyword("default"))
+ self.assertEquals(self._SingleTokenForInput("array"),
+ _MakeLexTokenForKeyword("array"))
+
+ def testValidIdentifiers(self):
+ """Tests identifiers."""
+ self.assertEquals(self._SingleTokenForInput("abcd"),
+ _MakeLexToken("NAME", "abcd"))
+ self.assertEquals(self._SingleTokenForInput("AbC_d012_"),
+ _MakeLexToken("NAME", "AbC_d012_"))
+ self.assertEquals(self._SingleTokenForInput("_0123"),
+ _MakeLexToken("NAME", "_0123"))
+
+ def testInvalidIdentifiers(self):
+ with self.assertRaisesRegexp(
+ mojom.parse.lexer.LexError,
+ r"^my_file\.mojom:1: Error: Illegal character '\$'$"):
+ self._TokensForInput("$abc")
+ with self.assertRaisesRegexp(
+ mojom.parse.lexer.LexError,
+ r"^my_file\.mojom:1: Error: Illegal character '\$'$"):
+ self._TokensForInput("a$bc")
+
+ def testDecimalIntegerConstants(self):
+ self.assertEquals(self._SingleTokenForInput("0"),
+ _MakeLexToken("INT_CONST_DEC", "0"))
+ self.assertEquals(self._SingleTokenForInput("1"),
+ _MakeLexToken("INT_CONST_DEC", "1"))
+ self.assertEquals(self._SingleTokenForInput("123"),
+ _MakeLexToken("INT_CONST_DEC", "123"))
+ self.assertEquals(self._SingleTokenForInput("10"),
+ _MakeLexToken("INT_CONST_DEC", "10"))
+
+ def testValidTokens(self):
+ """Tests valid tokens (which aren't tested elsewhere)."""
+ # Keywords tested in |testValidKeywords|.
+ # NAME tested in |testValidIdentifiers|.
+ self.assertEquals(self._SingleTokenForInput("@123"),
+ _MakeLexToken("ORDINAL", "@123"))
+ self.assertEquals(self._SingleTokenForInput("456"),
+ _MakeLexToken("INT_CONST_DEC", "456"))
+ self.assertEquals(self._SingleTokenForInput("0x01aB2eF3"),
+ _MakeLexToken("INT_CONST_HEX", "0x01aB2eF3"))
+ self.assertEquals(self._SingleTokenForInput("123.456"),
+ _MakeLexToken("FLOAT_CONST", "123.456"))
+ self.assertEquals(self._SingleTokenForInput("\"hello\""),
+ _MakeLexToken("STRING_LITERAL", "\"hello\""))
+ self.assertEquals(self._SingleTokenForInput("+"),
+ _MakeLexToken("PLUS", "+"))
+ self.assertEquals(self._SingleTokenForInput("-"),
+ _MakeLexToken("MINUS", "-"))
+ self.assertEquals(self._SingleTokenForInput("&"),
+ _MakeLexToken("AMP", "&"))
+ self.assertEquals(self._SingleTokenForInput("?"),
+ _MakeLexToken("QSTN", "?"))
+ self.assertEquals(self._SingleTokenForInput("="),
+ _MakeLexToken("EQUALS", "="))
+ self.assertEquals(self._SingleTokenForInput("=>"),
+ _MakeLexToken("RESPONSE", "=>"))
+ self.assertEquals(self._SingleTokenForInput("("),
+ _MakeLexToken("LPAREN", "("))
+ self.assertEquals(self._SingleTokenForInput(")"),
+ _MakeLexToken("RPAREN", ")"))
+ self.assertEquals(self._SingleTokenForInput("["),
+ _MakeLexToken("LBRACKET", "["))
+ self.assertEquals(self._SingleTokenForInput("]"),
+ _MakeLexToken("RBRACKET", "]"))
+ self.assertEquals(self._SingleTokenForInput("{"),
+ _MakeLexToken("LBRACE", "{"))
+ self.assertEquals(self._SingleTokenForInput("}"),
+ _MakeLexToken("RBRACE", "}"))
+ self.assertEquals(self._SingleTokenForInput("<"),
+ _MakeLexToken("LANGLE", "<"))
+ self.assertEquals(self._SingleTokenForInput(">"),
+ _MakeLexToken("RANGLE", ">"))
+ self.assertEquals(self._SingleTokenForInput(";"),
+ _MakeLexToken("SEMI", ";"))
+ self.assertEquals(self._SingleTokenForInput(","),
+ _MakeLexToken("COMMA", ","))
+ self.assertEquals(self._SingleTokenForInput("."),
+ _MakeLexToken("DOT", "."))
+
+ def _TokensForInput(self, input_string):
+ """Gets a list of tokens for the given input string."""
+ lexer = self._zygote_lexer.clone()
+ lexer.input(input_string)
+ rv = []
+ while True:
+ tok = lexer.token()
+ if not tok:
+ return rv
+ rv.append(tok)
+
+ def _SingleTokenForInput(self, input_string):
+ """Gets the single token for the given input string. (Raises an exception if
+ the input string does not result in exactly one token.)"""
+ toks = self._TokensForInput(input_string)
+ assert len(toks) == 1
+ return toks[0]
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
new file mode 100644
index 0000000..8671b1a
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
@@ -0,0 +1,1058 @@
+# 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 imp
+import os.path
+import sys
+import unittest
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("mojom")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.ast as ast
+import mojom.parse.lexer as lexer
+import mojom.parse.parser as parser
+
+
+class ParserTest(unittest.TestCase):
+ """Tests |parser.Parse()|."""
+
+ def testTrivialValidSource(self):
+ """Tests a trivial, but valid, .mojom source."""
+
+ source = """\
+ // This is a comment.
+
+ module my_module {
+ }
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testSourceWithCrLfs(self):
+ """Tests a .mojom source with CR-LFs instead of LFs."""
+
+ source = "// This is a comment.\r\n\r\nmodule my_module {\r\n}\r\n"
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testUnexpectedEOF(self):
+ """Tests a "truncated" .mojom source."""
+
+ source = """\
+ // This is a comment.
+
+ module my_module {
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom: Error: Unexpected end of file$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testCommentLineNumbers(self):
+ """Tests that line numbers are correctly tracked when comments are
+ present."""
+
+ source1 = """\
+ // Isolated C++-style comments.
+
+ // Foo.
+ asdf1
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\n *asdf1$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ // Consecutive C++-style comments.
+ // Foo.
+ // Bar.
+
+ struct Yada { // Baz.
+ // Quux.
+ int32 x;
+ };
+
+ asdf2
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\n *asdf2$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ /* Single-line C-style comments. */
+ /* Foobar. */
+
+ /* Baz. */
+ asdf3
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\n *asdf3$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ source4 = """\
+ /* Multi-line C-style comments.
+ */
+ /*
+ Foo.
+ Bar.
+ */
+
+ /* Baz
+ Quux. */
+ asdf4
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\n *asdf4$"):
+ parser.Parse(source4, "my_file.mojom")
+
+
+ def testSimpleStruct(self):
+ """Tests a simple .mojom source that just defines a struct."""
+
+ source = """\
+ module my_module {
+
+ struct MyStruct {
+ int32 a;
+ double b;
+ };
+
+ } // module my_module
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, 'int32', None),
+ ast.StructField('b', None, 'double', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testSimpleStructWithoutModule(self):
+ """Tests a simple struct without an enclosing module."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ double b;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, 'int32', None),
+ ast.StructField('b', None, 'double', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidStructDefinitions(self):
+ """Tests all types of definitions that can occur in a struct."""
+
+ source = """\
+ struct MyStruct {
+ enum MyEnum { VALUE };
+ const double kMyConst = 1.23;
+ int32 a;
+ SomeOtherStruct b; // Invalidity detected at another stage.
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.Enum('MyEnum',
+ ast.EnumValueList(ast.EnumValue('VALUE', None))),
+ ast.Const('kMyConst', 'double', '1.23'),
+ ast.StructField('a', None, 'int32', None),
+ ast.StructField('b', None, 'SomeOtherStruct', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidStructDefinitions(self):
+ """Tests that definitions that aren't allowed in a struct are correctly
+ detected."""
+
+ source1 = """\
+ struct MyStruct {
+ MyMethod(int32 a);
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '\(':\n"
+ r" *MyMethod\(int32 a\);$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ struct MyInnerStruct {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyInnerStruct {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ interface MyInterface {
+ MyMethod(int32 a);
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'interface':\n"
+ r" *interface MyInterface {$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testMissingModuleName(self):
+ """Tests an (invalid) .mojom with a missing module name."""
+
+ source1 = """\
+ // Missing module name.
+ module {
+ struct MyStruct {
+ int32 a;
+ };
+ }
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '{':\n *module {$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # Another similar case, but make sure that line-number tracking/reporting
+ # is correct.
+ source2 = """\
+ module
+ // This line intentionally left unblank.
+
+ {
+ }
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected '{':\n *{$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testEnums(self):
+ """Tests that enum statements are correctly parsed."""
+
+ source = """\
+ module my_module {
+ enum MyEnum1 { VALUE1, VALUE2 }; // No trailing comma.
+ enum MyEnum2 {
+ VALUE1 = -1,
+ VALUE2 = 0,
+ VALUE3 = + 987, // Check that space is allowed.
+ VALUE4 = 0xAF12,
+ VALUE5 = -0x09bcd,
+ VALUE6 = VALUE5,
+ VALUE7, // Leave trailing comma.
+ };
+ } // my_module
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Enum(
+ 'MyEnum1',
+ ast.EnumValueList([ast.EnumValue('VALUE1', None),
+ ast.EnumValue('VALUE2', None)])),
+ ast.Enum(
+ 'MyEnum2',
+ ast.EnumValueList([ast.EnumValue('VALUE1', '-1'),
+ ast.EnumValue('VALUE2', '0'),
+ ast.EnumValue('VALUE3', '+987'),
+ ast.EnumValue('VALUE4', '0xAF12'),
+ ast.EnumValue('VALUE5', '-0x09bcd'),
+ ast.EnumValue('VALUE6', ('IDENTIFIER',
+ 'VALUE5')),
+ ast.EnumValue('VALUE7', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidEnumInitializers(self):
+ """Tests that invalid enum initializers are correctly detected."""
+
+ # No values.
+ source1 = """\
+ enum MyEnum {
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '}':\n"
+ r" *};$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # Floating point value.
+ source2 = "enum MyEnum { VALUE = 0.123 };"
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:1: Error: Unexpected '0\.123':\n"
+ r"enum MyEnum { VALUE = 0\.123 };$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ # Boolean value.
+ source2 = "enum MyEnum { VALUE = true };"
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:1: Error: Unexpected 'true':\n"
+ r"enum MyEnum { VALUE = true };$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testConsts(self):
+ """Tests some constants and struct members initialized with them."""
+
+ source = """\
+ module my_module {
+
+ struct MyStruct {
+ const int8 kNumber = -1;
+ int8 number@0 = kNumber;
+ };
+
+ } // my_module
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody(
+ [ast.Const('kNumber', 'int8', '-1'),
+ ast.StructField('number', ast.Ordinal(0), 'int8',
+ ('IDENTIFIER', 'kNumber'))]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testNoConditionals(self):
+ """Tests that ?: is not allowed."""
+
+ source = """\
+ module my_module {
+
+ enum MyEnum {
+ MY_ENUM_1 = 1 ? 2 : 3
+ };
+
+ } // my_module
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected '\?':\n"
+ r" *MY_ENUM_1 = 1 \? 2 : 3$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testSimpleOrdinals(self):
+ """Tests that (valid) ordinal values are scanned correctly."""
+
+ source = """\
+ module my_module {
+
+ // This isn't actually valid .mojom, but the problem (missing ordinals)
+ // should be handled at a different level.
+ struct MyStruct {
+ int32 a0@0;
+ int32 a1@1;
+ int32 a2@2;
+ int32 a9@9;
+ int32 a10 @10;
+ int32 a11 @11;
+ int32 a29 @29;
+ int32 a1234567890 @1234567890;
+ };
+
+ } // module my_module
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a0', ast.Ordinal(0), 'int32', None),
+ ast.StructField('a1', ast.Ordinal(1), 'int32', None),
+ ast.StructField('a2', ast.Ordinal(2), 'int32', None),
+ ast.StructField('a9', ast.Ordinal(9), 'int32', None),
+ ast.StructField('a10', ast.Ordinal(10), 'int32', None),
+ ast.StructField('a11', ast.Ordinal(11), 'int32', None),
+ ast.StructField('a29', ast.Ordinal(29), 'int32', None),
+ ast.StructField('a1234567890', ast.Ordinal(1234567890),
+ 'int32', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidOrdinals(self):
+ """Tests that (lexically) invalid ordinals are correctly detected."""
+
+ source1 = """\
+ module my_module {
+
+ struct MyStruct {
+ int32 a_missing@;
+ };
+
+ } // module my_module
+ """
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:4: Error: Missing ordinal value$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ module my_module {
+
+ struct MyStruct {
+ int32 a_octal@01;
+ };
+
+ } // module my_module
+ """
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:4: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ module my_module { struct MyStruct { int32 a_invalid_octal@08; }; }
+ """
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ source4 = "module my_module { struct MyStruct { int32 a_hex@0x1aB9; }; }"
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source4, "my_file.mojom")
+
+ source5 = "module my_module { struct MyStruct { int32 a_hex@0X0; }; }"
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source5, "my_file.mojom")
+
+ source6 = """\
+ struct MyStruct {
+ int32 a_too_big@999999999999;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: "
+ r"Ordinal value 999999999999 too large:\n"
+ r" *int32 a_too_big@999999999999;$"):
+ parser.Parse(source6, "my_file.mojom")
+
+ def testNestedNamespace(self):
+ """Tests that "nested" namespaces work."""
+
+ source = """\
+ module my.mod {
+
+ struct MyStruct {
+ int32 a;
+ };
+
+ } // module my.mod
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my.mod'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(ast.StructField('a', None, 'int32', None)))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidHandleTypes(self):
+ """Tests (valid) handle types."""
+
+ source = """\
+ struct MyStruct {
+ handle a;
+ handle<data_pipe_consumer> b;
+ handle <data_pipe_producer> c;
+ handle < message_pipe > d;
+ handle
+ < shared_buffer
+ > e;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, 'handle', None),
+ ast.StructField('b', None, 'handle<data_pipe_consumer>', None),
+ ast.StructField('c', None, 'handle<data_pipe_producer>', None),
+ ast.StructField('d', None, 'handle<message_pipe>', None),
+ ast.StructField('e', None, 'handle<shared_buffer>', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidHandleType(self):
+ """Tests an invalid (unknown) handle type."""
+
+ source = """\
+ struct MyStruct {
+ handle<wtf_is_this> foo;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: "
+ r"Invalid handle type 'wtf_is_this':\n"
+ r" *handle<wtf_is_this> foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testValidDefaultValues(self):
+ """Tests default values that are valid (to the parser)."""
+
+ source = """\
+ struct MyStruct {
+ int16 a0 = 0;
+ uint16 a1 = 0x0;
+ uint16 a2 = 0x00;
+ uint16 a3 = 0x01;
+ uint16 a4 = 0xcd;
+ int32 a5 = 12345;
+ int64 a6 = -12345;
+ int64 a7 = +12345;
+ uint32 a8 = 0x12cd3;
+ uint32 a9 = -0x12cD3;
+ uint32 a10 = +0x12CD3;
+ bool a11 = true;
+ bool a12 = false;
+ float a13 = 1.2345;
+ float a14 = -1.2345;
+ float a15 = +1.2345;
+ float a16 = 123.;
+ float a17 = .123;
+ double a18 = 1.23E10;
+ double a19 = 1.E-10;
+ double a20 = .5E+10;
+ double a21 = -1.23E10;
+ double a22 = +.123E10;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a0', None, 'int16', '0'),
+ ast.StructField('a1', None, 'uint16', '0x0'),
+ ast.StructField('a2', None, 'uint16', '0x00'),
+ ast.StructField('a3', None, 'uint16', '0x01'),
+ ast.StructField('a4', None, 'uint16', '0xcd'),
+ ast.StructField('a5' , None, 'int32', '12345'),
+ ast.StructField('a6', None, 'int64', '-12345'),
+ ast.StructField('a7', None, 'int64', '+12345'),
+ ast.StructField('a8', None, 'uint32', '0x12cd3'),
+ ast.StructField('a9', None, 'uint32', '-0x12cD3'),
+ ast.StructField('a10', None, 'uint32', '+0x12CD3'),
+ ast.StructField('a11', None, 'bool', 'true'),
+ ast.StructField('a12', None, 'bool', 'false'),
+ ast.StructField('a13', None, 'float', '1.2345'),
+ ast.StructField('a14', None, 'float', '-1.2345'),
+ ast.StructField('a15', None, 'float', '+1.2345'),
+ ast.StructField('a16', None, 'float', '123.'),
+ ast.StructField('a17', None, 'float', '.123'),
+ ast.StructField('a18', None, 'double', '1.23E10'),
+ ast.StructField('a19', None, 'double', '1.E-10'),
+ ast.StructField('a20', None, 'double', '.5E+10'),
+ ast.StructField('a21', None, 'double', '-1.23E10'),
+ ast.StructField('a22', None, 'double', '+.123E10')]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidFixedSizeArray(self):
+ """Tests parsing a fixed size array."""
+
+ source = """\
+ struct MyStruct {
+ array<int32> normal_array;
+ array<int32, 1> fixed_size_array_one_entry;
+ array<int32, 10> fixed_size_array_ten_entries;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('normal_array', None, 'int32[]', None),
+ ast.StructField('fixed_size_array_one_entry', None, 'int32[1]',
+ None),
+ ast.StructField('fixed_size_array_ten_entries', None,
+ 'int32[10]', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidNestedArray(self):
+ """Tests parsing a nested array."""
+
+ source = "struct MyStruct { array<array<int32>> nested_array; };"
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ ast.StructField('nested_array', None, 'int32[][]', None)))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidFixedArraySize(self):
+ """Tests that invalid fixed array bounds are correctly detected."""
+
+ source1 = """\
+ struct MyStruct {
+ array<int32, 0> zero_size_array;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Fixed array size 0 invalid\n"
+ r" *array<int32, 0> zero_size_array;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ array<int32, 999999999999> too_big_array;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Fixed array size 999999999999 invalid\n"
+ r" *array<int32, 999999999999> too_big_array;$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ array<int32, abcdefg> not_a_number;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'abcdefg':\n"
+ r" *array<int32, abcdefg> not_a_number;"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidMethod(self):
+ """Tests parsing method declarations."""
+
+ source1 = "interface MyInterface { MyMethod(int32 a); };"
+ expected1 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Interface(
+ 'MyInterface',
+ None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod',
+ None,
+ ast.ParameterList(ast.Parameter('a', None, 'int32')),
+ None)))])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ source2 = """\
+ interface MyInterface {
+ MyMethod1@0(int32 a@0, int64 b@1);
+ MyMethod2@1() => ();
+ };
+ """
+ expected2 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Interface(
+ 'MyInterface',
+ None,
+ ast.InterfaceBody(
+ [ast.Method(
+ 'MyMethod1',
+ ast.Ordinal(0),
+ ast.ParameterList([ast.Parameter('a', ast.Ordinal(0),
+ 'int32'),
+ ast.Parameter('b', ast.Ordinal(1),
+ 'int64')]),
+ None),
+ ast.Method(
+ 'MyMethod2',
+ ast.Ordinal(1),
+ ast.ParameterList(),
+ ast.ParameterList())]))])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ source3 = """\
+ interface MyInterface {
+ MyMethod(string a) => (int32 a, bool b);
+ };
+ """
+ expected3 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Interface(
+ 'MyInterface',
+ None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod',
+ None,
+ ast.ParameterList(ast.Parameter('a', None, 'string')),
+ ast.ParameterList([ast.Parameter('a', None, 'int32'),
+ ast.Parameter('b', None, 'bool')]))))])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ def testInvalidMethods(self):
+ """Tests that invalid method declarations are correctly detected."""
+
+ # No trailing commas.
+ source1 = """\
+ interface MyInterface {
+ MyMethod(string a,);
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '\)':\n"
+ r" *MyMethod\(string a,\);$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # No leading commas.
+ source2 = """\
+ interface MyInterface {
+ MyMethod(, string a);
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected ',':\n"
+ r" *MyMethod\(, string a\);$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testValidInterfaceDefinitions(self):
+ """Tests all types of definitions that can occur in an interface."""
+
+ source = """\
+ interface MyInterface {
+ enum MyEnum { VALUE };
+ const int32 kMyConst = 123;
+ MyMethod(int32 x) => (MyEnum y);
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Interface(
+ 'MyInterface',
+ None,
+ ast.InterfaceBody(
+ [ast.Enum('MyEnum',
+ ast.EnumValueList(ast.EnumValue('VALUE', None))),
+ ast.Const('kMyConst', 'int32', '123'),
+ ast.Method(
+ 'MyMethod',
+ None,
+ ast.ParameterList(ast.Parameter('x', None, 'int32')),
+ ast.ParameterList(ast.Parameter('y', None, 'MyEnum')))]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidInterfaceDefinitions(self):
+ """Tests that definitions that aren't allowed in an interface are correctly
+ detected."""
+
+ source1 = """\
+ interface MyInterface {
+ struct MyStruct {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ interface MyInterface {
+ interface MyInnerInterface {
+ MyMethod(int32 x);
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'interface':\n"
+ r" *interface MyInnerInterface {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ interface MyInterface {
+ int32 my_field;
+ };
+ """
+ # The parser thinks that "int32" is a plausible name for a method, so it's
+ # "my_field" that gives it away.
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'my_field':\n"
+ r" *int32 my_field;$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidAttributes(self):
+ """Tests parsing attributes (and attribute lists)."""
+
+ # Note: We use structs because they have (optional) attribute lists.
+
+ # Empty attribute list.
+ source1 = "[] struct MyStruct {};"
+ expected1 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct('MyStruct', ast.AttributeList(), ast.StructBody())])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ # One-element attribute list, with name value.
+ source2 = "[MyAttribute=MyName] struct MyStruct {};"
+ expected2 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ ast.AttributeList(ast.Attribute("MyAttribute", "MyName")),
+ ast.StructBody())])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ # Two-element attribute list, with one string value and one integer value.
+ source3 = "[MyAttribute1 = \"hello\", MyAttribute2 = 5] struct MyStruct {};"
+ expected3 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ ast.AttributeList([ast.Attribute("MyAttribute1", "hello"),
+ ast.Attribute("MyAttribute2", 5)]),
+ ast.StructBody())])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()|
+ # literal (non-name) values, which is extremely dubious.)
+
+ def testInvalidAttributes(self):
+ """Tests that invalid attributes and attribute lists are correctly
+ detected."""
+
+ # Trailing commas not allowed.
+ source1 = "[MyAttribute=MyName,] struct MyStruct {};"
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:1: Error: Unexpected '\]':\n"
+ r"\[MyAttribute=MyName,\] struct MyStruct {};$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # Missing value.
+ source2 = "[MyAttribute=] struct MyStruct {};"
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:1: Error: Unexpected '\]':\n"
+ r"\[MyAttribute=\] struct MyStruct {};$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ # Missing key.
+ source3 = "[=MyName] struct MyStruct {};"
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:1: Error: Unexpected '=':\n"
+ r"\[=MyName\] struct MyStruct {};$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidImports(self):
+ """Tests parsing import statements."""
+
+ # One import (no module statement).
+ source1 = "import \"somedir/my.mojom\""
+ expected1 = ast.Mojom(
+ None,
+ ast.ImportList(ast.Import("somedir/my.mojom")),
+ [])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ # Two imports (no module statement).
+ source2 = """\
+ import "somedir/my1.mojom"
+ import "somedir/my2.mojom"
+ """
+ expected2 = ast.Mojom(
+ None,
+ ast.ImportList([ast.Import("somedir/my1.mojom"),
+ ast.Import("somedir/my2.mojom")]),
+ [])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ # Imports with module statement.
+ source3 = """\
+ import "somedir/my1.mojom"
+ import "somedir/my2.mojom"
+ module my_module {}
+ """
+ expected3 = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList([ast.Import("somedir/my1.mojom"),
+ ast.Import("somedir/my2.mojom")]),
+ [])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ def testInvalidImports(self):
+ """Tests that invalid import statements are correctly detected."""
+
+ source1 = """\
+ // Make the error occur on line 2.
+ import invalid
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'invalid':\n"
+ r" *import invalid$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ import // Missing string.
+ module {}
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'module':\n"
+ r" *module {}$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testValidNullableTypes(self):
+ """Tests parsing nullable types."""
+
+ source = """\
+ struct MyStruct {
+ int32? a; // This is actually invalid, but handled at a different
+ // level.
+ string? b;
+ array<int32> ? c;
+ array<string ? > ? d;
+ array<array<int32>?>? e;
+ array<int32, 1>? f;
+ array<string?, 1>? g;
+ some_struct? h;
+ handle? i;
+ handle<data_pipe_consumer>? j;
+ handle<data_pipe_producer>? k;
+ handle<message_pipe>? l;
+ handle<shared_buffer>? m;
+ some_interface&? n;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, 'int32?', None),
+ ast.StructField('b', None, 'string?', None),
+ ast.StructField('c', None, 'int32[]?', None),
+ ast.StructField('d', None, 'string?[]?', None),
+ ast.StructField('e', None, 'int32[]?[]?', None),
+ ast.StructField('f', None, 'int32[1]?', None),
+ ast.StructField('g', None, 'string?[1]?', None),
+ ast.StructField('h', None, 'some_struct?', None),
+ ast.StructField('i', None, 'handle?', None),
+ ast.StructField('j', None, 'handle<data_pipe_consumer>?',
+ None),
+ ast.StructField('k', None, 'handle<data_pipe_producer>?',
+ None),
+ ast.StructField('l', None, 'handle<message_pipe>?', None),
+ ast.StructField('m', None, 'handle<shared_buffer>?', None),
+ ast.StructField('n', None, 'some_interface&?', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidNullableTypes(self):
+ """Tests that invalid nullable types are correctly detected."""
+ source1 = """\
+ struct MyStruct {
+ string?? a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '\?':\n"
+ r" *string\?\? a;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ handle?<data_pipe_consumer> a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '<':\n"
+ r" *handle\?<data_pipe_consumer> a;$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ some_interface?& a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '&':\n"
+ r" *some_interface\?& a;$"):
+ parser.Parse(source3, "my_file.mojom")
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
new file mode 100755
index 0000000..b160de6
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# 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.
+
+"""Simple testing utility to just run the mojom parser."""
+
+
+import os.path
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ os.path.pardir, os.path.pardir))
+
+from mojom.parse.parser import Parse, ParseError
+
+
+def main(argv):
+ if len(argv) < 2:
+ print "usage: %s filename" % argv[0]
+ return 0
+
+ for filename in argv[1:]:
+ with open(filename) as f:
+ print "%s:" % filename
+ try:
+ print Parse(f.read(), filename)
+ except ParseError, e:
+ print e
+ return 1
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
new file mode 100755
index 0000000..899d40e
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# 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.
+
+"""Simple testing utility to just run the mojom translate stage."""
+
+
+import os.path
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ os.path.pardir, os.path.pardir))
+
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+def main(argv):
+ if len(argv) < 2:
+ print "usage: %s filename" % sys.argv[0]
+ return 1
+
+ for filename in argv[1:]:
+ with open(filename) as f:
+ print "%s:" % filename
+ print Translate(Parse(f.read(), filename),
+ os.path.splitext(os.path.basename(filename))[0])
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
new file mode 100644
index 0000000..2a4b17b
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
@@ -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 fnmatch
+from os import walk
+from os.path import join
+import sys
+
+
+def FindFiles(top, pattern, **kwargs):
+ """Finds files under |top| matching the glob pattern |pattern|, returning a
+ list of paths."""
+ matches = []
+ for dirpath, _, filenames in walk(top, **kwargs):
+ for filename in fnmatch.filter(filenames, pattern):
+ matches.append(join(dirpath, filename))
+ return matches
+
+
+def main(argv):
+ if len(argv) != 3:
+ print "usage: %s path pattern" % argv[0]
+ return 1
+
+ for filename in FindFiles(argv[1], argv[2]):
+ print filename
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
new file mode 100644
index 0000000..20ef461
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
@@ -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 os.path
+from subprocess import check_call
+import sys
+
+
+def RunBindingsGenerator(out_dir, root_dir, mojom_file, extra_flags=None):
+ out_dir = os.path.abspath(out_dir)
+ root_dir = os.path.abspath(root_dir)
+ mojom_file = os.path.abspath(mojom_file)
+
+ # The mojom file should be under the root directory somewhere.
+ assert mojom_file.startswith(root_dir)
+ mojom_reldir = os.path.dirname(os.path.relpath(mojom_file, root_dir))
+
+ # TODO(vtl): Abstract out the "main" functions, so that we can just import
+ # the bindings generator (which would be more portable and easier to use in
+ # tests).
+ this_dir = os.path.dirname(os.path.abspath(__file__))
+ # We're in src/mojo/public/tools/bindings/pylib/mojom_tests/support;
+ # mojom_bindings_generator.py is in .../bindings.
+ bindings_generator = os.path.join(this_dir, os.pardir, os.pardir, os.pardir,
+ "mojom_bindings_generator.py")
+
+ args = ["python", bindings_generator,
+ "-o", os.path.join(out_dir, mojom_reldir)]
+ if extra_flags:
+ args.extend(extra_flags)
+ args.append(mojom_file)
+
+ check_call(args)
+
+
+def main(argv):
+ if len(argv) < 4:
+ print "usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0]
+ return 1
+
+ RunBindingsGenerator(argv[1], argv[2], argv[3], extra_flags=argv[4:])
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))