summaryrefslogtreecommitdiff
path: root/mojo/public/tools/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/public/tools/bindings')
-rw-r--r--mojo/public/tools/bindings/BUILD.gn77
-rw-r--r--mojo/public/tools/bindings/README.md749
-rw-r--r--mojo/public/tools/bindings/blink_bindings_configuration.gni33
-rw-r--r--mojo/public/tools/bindings/chromium_bindings_configuration.gni83
-rwxr-xr-xmojo/public/tools/bindings/format_typemap_generator_args.py34
-rwxr-xr-xmojo/public/tools/bindings/generate_type_mappings.py148
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl131
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl29
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl65
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl448
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl49
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl16
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl41
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl96
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl64
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl212
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl111
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl236
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl118
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl30
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl46
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl70
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl161
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl57
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl32
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl14
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl92
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl12
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl56
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl47
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl141
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl24
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl47
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl82
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl94
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl39
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl20
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl80
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl85
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl41
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl3
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl12
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl418
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl42
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/header.java.tmpl14
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/interface.java.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl297
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/interface_internal.java.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/java_templates/union.java.tmpl4
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl33
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl198
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl70
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl49
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl126
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl154
-rw-r--r--mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl60
-rw-r--r--mojo/public/tools/bindings/generators/mojom_cpp_generator.py818
-rw-r--r--mojo/public/tools/bindings/generators/mojom_java_generator.py550
-rw-r--r--mojo/public/tools/bindings/generators/mojom_js_generator.py425
-rw-r--r--mojo/public/tools/bindings/mojom.gni661
-rwxr-xr-xmojo/public/tools/bindings/mojom_bindings_generator.py336
-rw-r--r--mojo/public/tools/bindings/mojom_bindings_generator_unittest.py23
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/error.py27
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/fileutil.py18
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py91
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/generator.py153
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py24
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/module.py891
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py34
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/pack.py250
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py193
-rwxr-xr-xmojo/public/tools/bindings/pylib/mojom/generate/run_tests.py35
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py67
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/test_support.py193
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/generate/translate.py639
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/parse/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/parse/ast.py410
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/parse/lexer.py254
-rw-r--r--mojo/public/tools/bindings/pylib/mojom/parse/parser.py461
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py55
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py156
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py37
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py48
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py136
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/parse/ast_unittest.py135
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py192
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py1497
-rwxr-xr-xmojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py36
-rwxr-xr-xmojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py34
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py80
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py0
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py32
-rw-r--r--mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py47
102 files changed, 14552 insertions, 0 deletions
diff --git a/mojo/public/tools/bindings/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn
new file mode 100644
index 0000000000..153d110332
--- /dev/null
+++ b/mojo/public/tools/bindings/BUILD.gn
@@ -0,0 +1,77 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+action("precompile_templates") {
+ sources = mojom_generator_sources
+ sources += [
+ "$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/enum_serialization_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_macros.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/module-shared-internal.h.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/module-shared.cc.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/module-shared.h.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/module.cc.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/module.h.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_data_view_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_data_view_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_macros.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_traits_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_traits_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_data_view_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_data_view_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_serialization_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_traits_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_traits_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/validation_macros.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/wrapper_class_template_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_declaration.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_template_definition.tmpl",
+ "$mojom_generator_root/generators/java_templates/constant_definition.tmpl",
+ "$mojom_generator_root/generators/java_templates/constants.java.tmpl",
+ "$mojom_generator_root/generators/java_templates/data_types_definition.tmpl",
+ "$mojom_generator_root/generators/java_templates/enum.java.tmpl",
+ "$mojom_generator_root/generators/java_templates/enum_definition.tmpl",
+ "$mojom_generator_root/generators/java_templates/header.java.tmpl",
+ "$mojom_generator_root/generators/java_templates/interface.java.tmpl",
+ "$mojom_generator_root/generators/java_templates/interface_definition.tmpl",
+ "$mojom_generator_root/generators/java_templates/interface_internal.java.tmpl",
+ "$mojom_generator_root/generators/java_templates/struct.java.tmpl",
+ "$mojom_generator_root/generators/java_templates/union.java.tmpl",
+ "$mojom_generator_root/generators/js_templates/enum_definition.tmpl",
+ "$mojom_generator_root/generators/js_templates/interface_definition.tmpl",
+ "$mojom_generator_root/generators/js_templates/module.amd.tmpl",
+ "$mojom_generator_root/generators/js_templates/module_definition.tmpl",
+ "$mojom_generator_root/generators/js_templates/struct_definition.tmpl",
+ "$mojom_generator_root/generators/js_templates/union_definition.tmpl",
+ "$mojom_generator_root/generators/js_templates/validation_macros.tmpl",
+ ]
+ script = mojom_generator_script
+ outputs = [
+ "$target_gen_dir/cpp_templates.zip",
+ "$target_gen_dir/java_templates.zip",
+ "$target_gen_dir/js_templates.zip",
+ ]
+ args = [
+ "--use_bundled_pylibs",
+ "precompile",
+ "-o",
+ rebase_path(target_gen_dir),
+ ]
+}
diff --git a/mojo/public/tools/bindings/README.md b/mojo/public/tools/bindings/README.md
new file mode 100644
index 0000000000..737c7e61df
--- /dev/null
+++ b/mojo/public/tools/bindings/README.md
@@ -0,0 +1,749 @@
+# ![Mojo Graphic](https://goo.gl/6CdlbH) Mojom IDL and Bindings Generator
+This document is a subset of the [Mojo documentation](/mojo).
+
+[TOC]
+
+## Overview
+
+Mojom is the IDL for Mojo bindings interfaces. Given a `.mojom` file, the
+[bindings generator](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings)
+outputs bindings for all supported languages: **C++**, **JavaScript**, and
+**Java**.
+
+For a trivial example consider the following hypothetical Mojom file we write to
+`//services/widget/public/interfaces/frobinator.mojom`:
+
+```
+module widget.mojom;
+
+interface Frobinator {
+ Frobinate();
+};
+```
+
+This defines a single [interface](#Interfaces) named `Frobinator` in a
+[module](#Modules) named `widget.mojom` (and thus fully qualified in Mojom as
+`widget.mojom.Frobinator`.) Note that many interfaces and/or other types of
+definitions may be included in a single Mojom file.
+
+If we add a corresponding GN target to
+`//services/widget/public/interfaces/BUILD.gn`:
+
+```
+import("mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+ sources = [
+ "frobinator.mojom",
+ ]
+}
+```
+
+and then build this target:
+
+```
+ninja -C out/r services/widget/public/interfaces
+```
+
+we'll find several generated sources in our output directory:
+
+```
+out/r/gen/services/widget/public/interfaces/frobinator.mojom.cc
+out/r/gen/services/widget/public/interfaces/frobinator.mojom.h
+out/r/gen/services/widget/public/interfaces/frobinator.mojom.js
+out/r/gen/services/widget/public/interfaces/frobinator.mojom.srcjar
+...
+```
+
+Each of these generated source modules includes a set of definitions
+representing the Mojom contents within the target language. For more details
+regarding the generated outputs please see
+[documentation for individual target languages](#Generated-Code-For-Target-Languages).
+
+## Mojom Syntax
+
+Mojom IDL allows developers to define **structs**, **unions**, **interfaces**,
+**constants**, and **enums**, all within the context of a **module**. These
+definitions are used to generate code in the supported target languages at build
+time.
+
+Mojom files may **import** other Mojom files in order to reference their
+definitions.
+
+### Primitive Types
+Mojom supports a few basic data types which may be composed into structs or used
+for message parameters.
+
+| Type | Description
+|-------------------------------|-------------------------------------------------------|
+| `bool` | Boolean type (`true` or `false`.)
+| `int8`, `uint8` | Signed or unsigned 8-bit integer.
+| `int16`, `uint16` | Signed or unsigned 16-bit integer.
+| `int32`, `uint32` | Signed or unsigned 32-bit integer.
+| `int64`, `uint64` | Signed or unsigned 64-bit integer.
+| `float`, `double` | 32- or 64-bit floating point number.
+| `string` | UTF-8 encoded string.
+| `array<T>` | Array of any Mojom type *T*; for example, `array<uint8>` or `array<array<string>>`.
+| `array<T, N>` | Fixed-length array of any Mojom type *T*. The parameter *N* must be an integral constant.
+| `map<S, T>` | Associated array maping values of type *S* to values of type *T*. *S* may be a `string`, `enum`, or numeric type.
+| `handle` | Generic Mojo handle. May be any type of handle, including a wrapped native platform handle.
+| `handle<message_pipe>` | Generic message pipe handle.
+| `handle<shared_buffer>` | Shared buffer handle.
+| `handle<data_pipe_producer>` | Data pipe producer handle.
+| `handle<data_pipe_consumer>` | Data pipe consumer handle.
+| *`InterfaceType`* | Any user-defined Mojom interface type. This is sugar for a strongly-typed message pipe handle which should eventually be used to make outgoing calls on the interface.
+| *`InterfaceType&`* | An interface request for any user-defined Mojom interface type. This is sugar for a more strongly-typed message pipe handle which is expected to receive request messages and should therefore eventually be bound to an implementation of the interface.
+| *`associated InterfaceType`* | An associated interface handle. See [Associated Interfaces](#Associated-Interfaces)
+| *`associated InterfaceType&`* | An associated interface request. See [Associated Interfaces](#Associated-Interfaces)
+| *T*? | An optional (nullable) value. Primitive numeric types (integers, floats, booleans, and enums) are not nullable. All other types are nullable.
+
+### Modules
+
+Every Mojom file may optionally specify a single **module** to which it belongs.
+
+This is used strictly for aggregaging all defined symbols therein within a
+common Mojom namespace. The specific impact this has on generated binidngs code
+varies for each target language. For example, if the following Mojom is used to
+generate bindings:
+
+```
+module business.stuff;
+
+interface MoneyGenerator {
+ GenerateMoney();
+};
+```
+
+Generated C++ bindings will define a class interface `MoneyGenerator` in the
+`business::stuff` namespace, while Java bindings will define an interface
+`MoneyGenerator` in the `org.chromium.business.stuff` package. JavaScript
+bindings at this time are unaffected by module declarations.
+
+**NOTE:** By convention in the Chromium codebase, **all** Mojom files should
+declare a module name with at least (and preferrably exactly) one top-level name
+as well as an inner `mojom` module suffix. *e.g.*, `chrome.mojom`,
+`business.mojom`, *etc.*
+
+This convention makes it easy to tell which symbols are generated by Mojom when
+reading non-Mojom code, and it also avoids namespace collisions in the fairly
+common scenario where you have a real C++ or Java `Foo` along with a
+corresponding Mojom `Foo` for its serialized representation.
+
+### Imports
+
+If your Mojom references definitions from other Mojom files, you must **import**
+those files. Import syntax is as follows:
+
+```
+import "services/widget/public/interfaces/frobinator.mojom";
+```
+
+Import paths are always relative to the top-level directory.
+
+Note that circular imports are **not** supported.
+
+### Structs
+
+Structs are defined using the **struct** keyword, and they provide a way to
+group related fields together:
+
+``` cpp
+struct StringPair {
+ string first;
+ string second;
+};
+```
+
+Struct fields may be comprised of any of the types listed above in the
+[Primitive Types](#Primitive-Types) section.
+
+Default values may be specified as long as they are constant:
+
+``` cpp
+struct Request {
+ int32 id = -1;
+ string details;
+};
+```
+
+What follows is a fairly
+comprehensive example using the supported field types:
+
+``` cpp
+struct StringPair {
+ string first;
+ string second;
+};
+
+enum AnEnum {
+ YES,
+ NO
+};
+
+interface SampleInterface {
+ DoStuff();
+};
+
+struct AllTheThings {
+ // Note that these types can never be marked nullable!
+ bool boolean_value;
+ int8 signed_8bit_value = 42;
+ uint8 unsigned_8bit_value;
+ int16 signed_16bit_value;
+ uint16 unsigned_16bit_value;
+ int32 signed_32bit_value;
+ uint32 unsigned_32bit_value;
+ int64 signed_64bit_value;
+ uint64 unsigned_64bit_value;
+ float float_value_32bit;
+ double float_value_64bit;
+ AnEnum enum_value = AnEnum.YES;
+
+ // Strings may be nullable.
+ string? maybe_a_string_maybe_not;
+
+ // Structs may contain other structs. These may also be nullable.
+ StringPair some_strings;
+ StringPair? maybe_some_more_strings;
+
+ // In fact structs can also be nested, though in practice you must always make
+ // such fields nullable -- otherwise messages would need to be infinitely long
+ // in order to pass validation!
+ AllTheThings? more_things;
+
+ // Arrays may be templated over any Mojom type, and are always nullable:
+ array<int32> numbers;
+ array<int32>? maybe_more_numbers;
+
+ // Arrays of arrays of arrays... are fine.
+ array<array<array<AnEnum>>> this_works_but_really_plz_stop;
+
+ // The element type may be nullable if it's a type which is allowed to be
+ // nullable.
+ array<AllTheThings?> more_maybe_things;
+
+ // Fixed-size arrays get some extra validation on the receiving end to ensure
+ // that the correct number of elements is always received.
+ array<uint64, 2> uuid;
+
+ // Maps follow many of the same rules as arrays. Key types may be any
+ // non-handle, non-collection type, and value types may be any supported
+ // struct field type. Maps may also be nullable.
+ map<string, int32> one_map;
+ map<AnEnum, string>? maybe_another_map;
+ map<StringPair, AllTheThings?>? maybe_a_pretty_weird_but_valid_map;
+ map<StringPair, map<int32, array<map<string, string>?>?>?> ridiculous;
+
+ // And finally, all handle types are valid as struct fields and may be
+ // nullable. Note that interfaces and interface requests (the "Foo" and
+ // "Foo&" type syntax respectively) are just strongly-typed message pipe
+ // handles.
+ handle generic_handle;
+ handle<data_pipe_consumer> reader;
+ handle<data_pipe_producer>? maybe_writer;
+ handle<shared_buffer> dumping_ground;
+ handle<message_pipe> raw_message_pipe;
+ SampleInterface? maybe_a_sample_interface_client_pipe;
+ SampleInterface& non_nullable_sample_interface_request;
+ SampleInterface&? nullable_sample_interface_request;
+ associated SampleInterface associated_interface_client;
+ associated SampleInterface& associated_interface_request;
+ assocaited SampleInterface&? maybe_another_associated_request;
+};
+```
+
+For details on how all of these different types translate to usable generated
+code, see
+[documentation for individual target languages](#Generated-Code-For-Target-Languages).
+
+### Enumeration Types
+
+Enumeration types may be defined using the **enum** keyword either directly
+within a module or within the namespace of some struct or interface:
+
+```
+module business.mojom;
+
+enum Department {
+ SALES = 0,
+ DEV,
+};
+
+struct Employee {
+ enum Type {
+ FULL_TIME,
+ PART_TIME,
+ };
+
+ Type type;
+ // ...
+};
+```
+
+That that similar to C-style enums, individual values may be explicitly assigned
+within an enum definition. By default values are based at zero and incremenet by
+1 sequentially.
+
+The effect of nested definitions on generated bindings varies depending on the
+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages)
+
+### Constants
+
+Constants may be defined using the **const** keyword either directly within a
+module or within the namespace of some struct or interface:
+
+```
+module business.mojom;
+
+const string kServiceName = "business";
+
+struct Employee {
+ const uint64 kInvalidId = 0;
+
+ enum Type {
+ FULL_TIME,
+ PART_TIME,
+ };
+
+ uint64 id = kInvalidId;
+ Type type;
+};
+```
+
+The effect of nested definitions on generated bindings varies depending on the
+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages)
+
+### Interfaces
+
+An **interface** is a logical bundle of parameterized request messages. Each
+request message may optionally define a parameterized response message. Here's
+syntax to define an interface `Foo` with various kinds of requests:
+
+```
+interface Foo {
+ // A request which takes no arguments and expects no response.
+ MyMessage();
+
+ // A request which has some arguments and expects no response.
+ MyOtherMessage(string name, array<uint8> bytes);
+
+ // A request which expects a single-argument response.
+ MyMessageWithResponse(string command) => (bool success);
+
+ // A request which expects a response with multiple arguments.
+ MyMessageWithMoarResponse(string a, string b) => (int8 c, int8 d);
+};
+```
+
+Anything which is a valid struct field type (see [Structs](#Structs)) is also a
+valid request or response argument type. The type notation is the same for both.
+
+### Attributes
+
+Mojom definitions may have their meaning altered by **attributes**, specified
+with a syntax similar to Java or C# attributes. There are a handle of
+interesting attributes supported today.
+
+**`[Sync]`**
+: The `Sync` attribute may be specified for any interface method which expects
+ a response. This makes it so that callers of the method can wait
+ synchronously for a response. See
+ [Synchronous Calls](/mojo/public/cpp/bindings#Synchronous-Calls) in the C++
+ bindings documentation. Note that sync calls are not currently supported in
+ other target languages.
+
+**`[Extensible]`**
+: The `Extensible` attribute may be specified for any enum definition. This
+ essentially disables builtin range validation when receiving values of the
+ enum type in a message, allowing older bindings to tolerate unrecognized
+ values from newer versions of the enum.
+
+**`[Native]`**
+: The `Native` attribute may be specified for an empty struct declaration to
+ provide a nominal bridge between Mojo IPC and legacy `IPC::ParamTraits` or
+ `IPC_STRUCT_TRAITS*` macros.
+ See [Using Legacy IPC Traits](/ipc#Using-Legacy-IPC-Traits) for more
+ details. Note support for this attribute is strictly limited to C++ bindings
+ generation.
+
+**`[MinVersion=N]`**
+: The `MinVersion` attribute is used to specify the version at which a given
+ field, enum value, interface method, or method parameter was introduced.
+ See [Versioning](#Versioning) for more details.
+
+## Generated Code For Target Languages
+
+When the bindings generator successfully processes an input Mojom file, it emits
+corresponding code for each supported target language. For more details on how
+Mojom concepts translate to a given target langauge, please refer to the
+bindings API documentation for that language:
+
+* [C++ Bindings](/mojo/public/cpp/bindings)
+* [JavaScript Bindings](/mojo/public/js)
+* [Java Bindings](/mojo/public/java/bindings)
+
+## Message Validation
+
+Regardless of target language, all interface messages are validated during
+deserialization before they are dispatched to a receiving implementation of the
+interface. This helps to ensure consitent validation across interfaces without
+leaving the burden to developers and security reviewers every time a new message
+is added.
+
+If a message fails validation, it is never dispatched. Instead a **connection
+error** is raised on the binding object (see
+[C++ Connection Errors](/mojo/public/cpp/bindings#Connection-Errors),
+[Java Connection Errors](/mojo/public/java/bindings#Connection-Errors), or
+[JavaScript Connection Errors](/mojo/public/js#Connection-Errors) for details.)
+
+Some baseline level of validation is done automatically for primitive Mojom
+types.
+
+### Non-Nullable Objects
+
+Mojom fields or parameter values (*e.g.*, structs, interfaces, arrays, *etc.*)
+may be marked nullable in Mojom definitions (see
+[Primitive Types](#Primitive-Types).) If a field or parameter is **not** marked
+nullable but a message is received with a null value in its place, that message
+will fail validation.
+
+### Enums
+
+Enums declared in Mojom are automatically validated against the range of legal
+values. For example if a Mojom declares the enum:
+
+``` cpp
+enum AdvancedBoolean {
+ TRUE = 0,
+ FALSE = 1,
+ FILE_NOT_FOUND = 2,
+};
+```
+
+and a message is received with the integral value 3 (or anything other than 0,
+1, or 2) in place of some `AdvancedBoolean` field or parameter, the message will
+fail validation.
+
+*** note
+NOTE: It's possible to avoid this type of validation error by explicitly marking
+an enum as [Extensible](#Attributes) if you anticipate your enum being exchanged
+between two different versions of the binding interface. See
+[Versioning](#Versioning).
+***
+
+### Other failures
+
+There are a host of internal validation errors that may occur when a malformed
+message is received, but developers should not be concerned with these
+specifically; in general they can only result from internal bindings bugs,
+compromised processes, or some remote endpoint making a dubious effort to
+manually encode their own bindings messages.
+
+### Custom Validation
+
+It's also possible for developers to define custom validation logic for specific
+Mojom struct types by exploiting the
+[type mapping](/mojo/public/cpp/bindings#Type-Mapping) system for C++ bindings.
+Messages rejected by custom validation logic trigger the same validation failure
+behavior as the built-in type validation routines.
+
+## Associated Interfaces
+
+As mentioned in the [Primitive Types](#Primitive-Types) section above, interface
+and interface request fields and parameters may be marked as `associated`. This
+essentially means that they are piggy-backed on some other interface's message
+pipe.
+
+Because individual interface message pipes operate independently there can be no
+relative ordering guarantees among them. Associated interfaces are useful when
+one interface needs to guarantee strict FIFO ordering with respect to one or
+more other interfaces, as they allow interfaces to share a single pipe.
+
+Currenly associated interfaces are only supported in generated C++ bindings.
+See the documentation for
+[C++ Associated Interfaces](/mojo/public/cpp/bindings#Associated-Interfaces).
+
+## Versioning
+
+### Overview
+
+*** note
+**NOTE:** You don't need to worry about versioning if you don't care about
+backwards compatibility. Specifically, all parts of Chrome are updated
+atomically today and there is not yet any possibility of any two Chrome
+processes communicating with two different versions of any given Mojom
+interface.
+***
+
+Services extend their interfaces to support new features over time, and clients
+want to use those new features when they are available. If services and clients
+are not updated at the same time, it's important for them to be able to
+communicate with each other using different snapshots (versions) of their
+interfaces.
+
+This document shows how to extend Mojom interfaces in a backwards-compatible
+way. Changing interfaces in a non-backwards-compatible way is not discussed,
+because in that case communication between different interface versions is
+impossible anyway.
+
+### Versioned Structs
+
+You can use the `MinVersion` [attribute](#Attributes) to indicate from which
+version a struct field is introduced. Assume you have the following struct:
+
+``` cpp
+struct Employee {
+ uint64 employee_id;
+ string name;
+};
+```
+
+and you would like to add a birthday field. You can do:
+
+``` cpp
+struct Employee {
+ uint64 employee_id;
+ string name;
+ [MinVersion=1] Date? birthday;
+};
+```
+
+By default, fields belong to version 0. New fields must be appended to the
+struct definition (*i.e*., existing fields must not change **ordinal value**)
+with the `MinVersion` attribute set to a number greater than any previous
+existing versions.
+
+**Ordinal value** refers to the relative positional layout of a struct's fields
+(and an interface's methods) when encoded in a message. Implicitly, ordinal
+numbers are assigned to fields according to lexical position. In the example
+above, `employee_id` has an ordinal value of 0 and `name` has an ordinal value
+of 1.
+
+Ordinal values can be specified explicitly using `**@**` notation, subject to
+the following hard constraints:
+
+* For any given struct or interface, if any field or method explicitly specifies
+ an ordinal value, all fields or methods must explicitly specify an ordinal
+ value.
+* For an *N*-field struct or *N*-method interface, the set of explicitly
+ assigned ordinal values must be limited to the range *[0, N-1]*.
+
+You may reorder fields, but you must ensure that the ordinal values of existing
+fields remain unchanged. For example, the following struct remains
+backwards-compatible:
+
+``` cpp
+struct Employee {
+ uint64 employee_id@0;
+ [MinVersion=1] Date? birthday@2;
+ string name@1;
+};
+```
+
+*** note
+**NOTE:** Newly added fields of Mojo object or handle types MUST be nullable.
+See [Primitive Types](#Primitive-Types).
+***
+
+### Versioned Interfaces
+
+There are two dimensions on which an interface can be extended
+
+**Appending New Parameters To Existing Methods**
+: Parameter lists are treated as structs internally, so all the rules of
+ versioned structs apply to method parameter lists. The only difference is
+ that the version number is scoped to the whole interface rather than to any
+ individual parameter list.
+
+ Please note that adding a response to a message which did not previously
+ expect a response is a not a backwards-compatible change.
+
+**Appending New Methods**
+: Similarly, you can reorder methods with explicit ordinal values as long as
+ the ordinal values of existing methods are unchanged.
+
+For example:
+
+``` cpp
+// Old version:
+interface HumanResourceDatabase {
+ AddEmployee(Employee employee) => (bool success);
+ QueryEmployee(uint64 id) => (Employee? employee);
+};
+
+// New version:
+interface HumanResourceDatabase {
+ AddEmployee(Employee employee) => (bool success);
+
+ QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print)
+ => (Employee? employee,
+ [MinVersion=1] array<uint8>? finger_print);
+
+ [MinVersion=1]
+ AttachFingerPrint(uint64 id, array<uint8> finger_print)
+ => (bool success);
+};
+```
+
+Similar to [versioned structs](#Versioned-Structs), when you pass the parameter
+list of a request or response method to a destination using an older version of
+an interface, unrecognized fields are silently discarded. However, if the method
+call itself is not recognized, it is considered a validation error and the
+receiver will close its end of the interface pipe. For example, if a client on
+version 1 of the above interface sends an `AttachFingerPrint` request to an
+implementation of version 0, the client will be disconnected.
+
+Bindings target languages that support versioning expose means to query or
+assert the remote version from a client handle (*e.g.*, an
+`InterfacePtr<T>` in C++ bindings.)
+
+See
+[C++ Versioning Considerations](/mojo/public/cpp/bindings#Versioning-Considerations)
+and [Java Versioning Considerations](/mojo/public/java/bindings#Versioning-Considerations)
+
+### Versioned Enums
+
+**By default, enums are non-extensible**, which means that generated message
+validation code does not expect to see new values in the future. When an unknown
+value is seen for a non-extensible enum field or parameter, a validation error
+is raised.
+
+If you want an enum to be extensible in the future, you can apply the
+`[Extensible]` [attribute](#Attributes):
+
+``` cpp
+[Extensible]
+enum Department {
+ SALES,
+ DEV,
+};
+```
+
+And later you can extend this enum without breaking backwards compatibility:
+
+``` cpp
+[Extensible]
+enum Department {
+ SALES,
+ DEV,
+ [MinVersion=1] RESEARCH,
+};
+```
+
+*** note
+**NOTE:** For versioned enum definitions, the use of a `[MinVersion]` attribute
+is strictly for documentation purposes. It has no impact on the generated code.
+***
+
+With extensible enums, bound interface implementations may receive unknown enum
+values and will need to deal with them gracefully. See
+[C++ Versioning Considerations](/mojo/public/cpp/bindings#Versioning-Considerations)
+for details.
+
+## Grammar Reference
+
+Below is the (BNF-ish) context-free grammar of the Mojom language:
+
+```
+MojomFile = StatementList
+StatementList = Statement StatementList | Statement
+Statement = ModuleStatement | ImportStatement | Definition
+
+ModuleStatement = AttributeSection "module" Identifier ";"
+ImportStatement = "import" StringLiteral ";"
+Definition = Struct Union Interface Enum Const
+
+AttributeSection = "[" AttributeList "]"
+AttributeList = <empty> | NonEmptyAttributeList
+NonEmptyAttributeList = Attribute
+ | Attribute "," NonEmptyAttributeList
+Attribute = Name
+ | Name "=" Name
+ | Name "=" Literal
+
+Struct = AttributeSection "struct" Name "{" StructBody "}" ";"
+ | AttributeSection "struct" Name ";"
+StructBody = <empty>
+ | StructBody Const
+ | StructBody Enum
+ | StructBody StructField
+StructField = AttributeSection TypeSpec Name Orginal Default ";"
+
+Union = AttributeSection "union" Name "{" UnionBody "}" ";"
+UnionBody = <empty> | UnionBody UnionField
+UnionField = AttributeSection TypeSpec Name Ordinal ";"
+
+Interface = AttributeSection "interface" Name "{" InterfaceBody "}" ";"
+InterfaceBody = <empty>
+ | InterfaceBody Const
+ | InterfaceBody Enum
+ | InterfaceBody Method
+Method = AttributeSection Name Ordinal "(" ParamterList ")" Response ";"
+ParameterList = <empty> | NonEmptyParameterList
+NonEmptyParameterList = Parameter
+ | Parameter "," NonEmptyParameterList
+Parameter = AttributeSection TypeSpec Name Ordinal
+Response = <empty> | "=>" "(" ParameterList ")"
+
+TypeSpec = TypeName "?" | TypeName
+TypeName = BasicTypeName
+ | Array
+ | FixedArray
+ | Map
+ | InterfaceRequest
+BasicTypeName = Identifier | "associated" Identifier | HandleType | NumericType
+NumericType = "bool" | "int8" | "uint8" | "int16" | "uint16" | "int32"
+ | "uint32" | "int64" | "uint64" | "float" | "double"
+HandleType = "handle" | "handle" "<" SpecificHandleType ">"
+SpecificHandleType = "message_pipe"
+ | "shared_buffer"
+ | "data_pipe_consumer"
+ | "data_pipe_producer"
+Array = "array" "<" TypeSpec ">"
+FixedArray = "array" "<" TypeSpec "," IntConstDec ">"
+Map = "map" "<" Identifier "," TypeSpec ">"
+InterfaceRequest = Identifier "&" | "associated" Identifier "&"
+
+Ordinal = <empty> | OrdinalValue
+
+Default = <empty> | "=" Constant
+
+Enum = AttributeSection "enum" Name "{" NonEmptyEnumValueList "}" ";"
+ | AttributeSection "enum" Name "{" NonEmptyEnumValueList "," "}" ";"
+NonEmptyEnumValueList = EnumValue | NonEmptyEnumValueList "," EnumValue
+EnumValue = AttributeSection Name
+ | AttributeSection Name "=" Integer
+ | AttributeSection Name "=" Identifier
+
+Const = "const" TypeSpec Name "=" Constant ";"
+
+Constant = Literal | Identifier ";"
+
+Identifier = Name | Name "." Identifier
+
+Literal = Integer | Float | "true" | "false" | "default" | StringLiteral
+
+Integer = IntConst | "+" IntConst | "-" IntConst
+IntConst = IntConstDec | IntConstHex
+
+Float = FloatConst | "+" FloatConst | "-" FloatConst
+
+; The rules below are for tokens matched strictly according to the given regexes
+
+Identifier = /[a-zA-Z_][0-9a-zA-Z_]*/
+IntConstDec = /0|(1-9[0-9]*)/
+IntConstHex = /0[xX][0-9a-fA-F]+/
+OrdinalValue = /@(0|(1-9[0-9]*))/
+FloatConst = ... # Imagine it's close enough to C-style float syntax.
+StringLiteral = ... # Imagine it's close enough to C-style string literals, including escapes.
+```
+
+## Additional Documentation
+
+[Mojom Message Format](https://docs.google.com/document/d/13pv9cFh5YKuBggDBQ1-AL8VReF-IYpFOFpRfvWFrwio/edit)
+: Describes the wire format used by Mojo bindings interfaces over message
+ pipes.
+
+[Input Format of Mojom Message Validation Tests](https://docs.google.com/document/d/1-y-2IYctyX2NPaLxJjpJfzVNWCC2SR2MJAD9MpIytHQ/edit)
+: Describes a text format used to facilitate bindings message validation
+ tests.
diff --git a/mojo/public/tools/bindings/blink_bindings_configuration.gni b/mojo/public/tools/bindings/blink_bindings_configuration.gni
new file mode 100644
index 0000000000..bb0fc432a3
--- /dev/null
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -0,0 +1,33 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+variant = "blink"
+
+for_blink = true
+
+_typemap_imports = [
+ "//mojo/public/cpp/bindings/tests/blink_typemaps.gni",
+ "//third_party/WebKit/Source/platform/mojo/blink_typemaps.gni",
+ "//third_party/WebKit/public/blink_typemaps.gni",
+ "//third_party/WebKit/public/public_typemaps.gni",
+]
+_typemaps = []
+
+foreach(typemap_import, _typemap_imports) {
+ # Avoid reassignment error by assigning to empty scope first.
+ _imported = {
+ }
+ _imported = read_file(typemap_import, "scope")
+ _typemaps += _imported.typemaps
+}
+
+typemaps = []
+foreach(typemap, _typemaps) {
+ typemaps += [ {
+ filename = typemap
+ config = read_file(typemap, "scope")
+ } ]
+}
+
+blacklist = []
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
new file mode 100644
index 0000000000..ca36723fb0
--- /dev/null
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -0,0 +1,83 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+_typemap_imports = [
+ "//ash/public/interfaces/typemaps.gni",
+ "//cc/ipc/typemaps.gni",
+ "//chrome/browser/media/router/mojo/typemaps.gni",
+ "//chrome/common/extensions/typemaps.gni",
+ "//chrome/common/importer/typemaps.gni",
+ "//chrome/typemaps.gni",
+ "//components/arc/common/typemaps.gni",
+ "//components/metrics/public/cpp/typemaps.gni",
+ "//components/typemaps.gni",
+ "//content/common/bluetooth/typemaps.gni",
+ "//content/common/indexed_db/typemaps.gni",
+ "//content/common/presentation/typemaps.gni",
+ "//content/common/typemaps.gni",
+ "//content/public/common/typemaps.gni",
+ "//device/bluetooth/public/interfaces/typemaps.gni",
+ "//device/gamepad/public/interfaces/typemaps.gni",
+ "//device/generic_sensor/public/interfaces/typemaps.gni",
+ "//device/usb/public/interfaces/typemaps.gni",
+ "//extensions/common/typemaps.gni",
+ "//gpu/ipc/common/typemaps.gni",
+ "//media/capture/mojo/typemaps.gni",
+ "//media/mojo/interfaces/typemaps.gni",
+ "//mojo/common/typemaps.gni",
+ "//mojo/public/cpp/bindings/tests/chromium_typemaps.gni",
+ "//net/interfaces/typemaps.gni",
+ "//services/preferences/public/cpp/typemaps.gni",
+ "//services/resource_coordinator/public/cpp/typemaps.gni",
+ "//services/service_manager/public/cpp/typemaps.gni",
+ "//services/ui/gpu/interfaces/typemaps.gni",
+ "//services/ui/public/interfaces/cursor/typemaps.gni",
+ "//services/ui/public/interfaces/ime/typemaps.gni",
+ "//services/video_capture/public/interfaces/typemaps.gni",
+ "//skia/public/interfaces/typemaps.gni",
+ "//third_party/WebKit/public/public_typemaps.gni",
+ "//ui/base/mojo/typemaps.gni",
+ "//ui/display/mojo/typemaps.gni",
+ "//ui/events/devices/mojo/typemaps.gni",
+ "//ui/events/mojo/typemaps.gni",
+ "//ui/gfx/typemaps.gni",
+ "//ui/latency/mojo/typemaps.gni",
+ "//ui/message_center/mojo/typemaps.gni",
+ "//url/mojo/typemaps.gni",
+]
+
+_typemap_imports_mac = [ "//content/common/typemaps_mac.gni" ]
+
+_typemaps = []
+foreach(typemap_import, _typemap_imports) {
+ # Avoid reassignment error by assigning to empty scope first.
+ _imported = {
+ }
+ _imported = read_file(typemap_import, "scope")
+ _typemaps += _imported.typemaps
+}
+
+typemaps = []
+foreach(typemap, _typemaps) {
+ typemaps += [ {
+ filename = typemap
+ config = read_file(typemap, "scope")
+ } ]
+}
+
+_typemaps_mac = []
+foreach(typemap_import, _typemap_imports_mac) {
+ _imported = {
+ }
+ _imported = read_file(typemap_import, "scope")
+ _typemaps_mac += _imported.typemaps
+}
+
+typemaps_mac = []
+foreach(typemap, _typemaps_mac) {
+ typemaps_mac += [ {
+ filename = typemap
+ config = read_file(typemap, "scope")
+ } ]
+}
diff --git a/mojo/public/tools/bindings/format_typemap_generator_args.py b/mojo/public/tools/bindings/format_typemap_generator_args.py
new file mode 100755
index 0000000000..5057d6cdac
--- /dev/null
+++ b/mojo/public/tools/bindings/format_typemap_generator_args.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+# This utility converts mojom dependencies into their corresponding typemap
+# paths and formats them to be consumed by generate_type_mappings.py.
+
+
+def FormatTypemap(typemap_filename):
+ # A simple typemap is valid Python with a minor alteration.
+ with open(typemap_filename) as f:
+ typemap_content = f.read().replace('=\n', '=')
+ typemap = {}
+ exec typemap_content in typemap
+
+ for header in typemap.get('public_headers', []):
+ yield 'public_headers=%s' % header
+ for header in typemap.get('traits_headers', []):
+ yield 'traits_headers=%s' % header
+ for header in typemap.get('type_mappings', []):
+ yield 'type_mappings=%s' % header
+
+
+def main():
+ typemaps = sys.argv[1:]
+ print ' '.join('--start-typemap %s' % ' '.join(FormatTypemap(typemap))
+ for typemap in typemaps)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/mojo/public/tools/bindings/generate_type_mappings.py b/mojo/public/tools/bindings/generate_type_mappings.py
new file mode 100755
index 0000000000..824f8045ab
--- /dev/null
+++ b/mojo/public/tools/bindings/generate_type_mappings.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Generates a JSON typemap from its command-line arguments and dependencies.
+
+Each typemap should be specified in an command-line argument of the form
+key=value, with an argument of "--start-typemap" preceding each typemap.
+
+For example,
+generate_type_mappings.py --output=foo.typemap --start-typemap \\
+ public_headers=foo.h traits_headers=foo_traits.h \\
+ type_mappings=mojom.Foo=FooImpl
+
+generates a foo.typemap containing
+{
+ "c++": {
+ "mojom.Foo": {
+ "typename": "FooImpl",
+ "traits_headers": [
+ "foo_traits.h"
+ ],
+ "public_headers": [
+ "foo.h"
+ ]
+ }
+ }
+}
+
+Then,
+generate_type_mappings.py --dependency foo.typemap --output=bar.typemap \\
+ --start-typemap public_headers=bar.h traits_headers=bar_traits.h \\
+ type_mappings=mojom.Bar=BarImpl
+
+generates a bar.typemap containing
+{
+ "c++": {
+ "mojom.Bar": {
+ "typename": "BarImpl",
+ "traits_headers": [
+ "bar_traits.h"
+ ],
+ "public_headers": [
+ "bar.h"
+ ]
+ },
+ "mojom.Foo": {
+ "typename": "FooImpl",
+ "traits_headers": [
+ "foo_traits.h"
+ ],
+ "public_headers": [
+ "foo.h"
+ ]
+ }
+ }
+}
+"""
+
+import argparse
+import json
+import os
+import re
+
+
+def ReadTypemap(path):
+ with open(path) as f:
+ return json.load(f)['c++']
+
+
+def ParseTypemapArgs(args):
+ typemaps = [s for s in '\n'.join(args).split('--start-typemap\n') if s]
+ result = {}
+ for typemap in typemaps:
+ result.update(ParseTypemap(typemap))
+ return result
+
+
+def ParseTypemap(typemap):
+ values = {'type_mappings': [], 'public_headers': [], 'traits_headers': []}
+ for line in typemap.split('\n'):
+ if not line:
+ continue
+ key, _, value = line.partition('=')
+ values[key].append(value.lstrip('/'))
+ result = {}
+ mapping_pattern = \
+ re.compile(r"""^([^=]+) # mojom type
+ =
+ ([^[]+) # native type
+ (?:\[([^]]+)\])?$ # optional attribute in square brackets
+ """, re.X)
+ for typename in values['type_mappings']:
+ match_result = mapping_pattern.match(typename)
+ assert match_result, (
+ "Cannot parse entry in the \"type_mappings\" section: %s" % typename)
+
+ mojom_type = match_result.group(1)
+ native_type = match_result.group(2)
+ attributes = []
+ if match_result.group(3):
+ attributes = match_result.group(3).split(',')
+
+ assert mojom_type not in result, (
+ "Cannot map multiple native types (%s, %s) to the same mojom type: %s" %
+ (result[mojom_type]['typename'], native_type, mojom_type))
+
+ result[mojom_type] = {
+ 'typename': native_type,
+ 'non_copyable_non_movable': 'non_copyable_non_movable' in attributes,
+ 'move_only': 'move_only' in attributes,
+ 'copyable_pass_by_value': 'copyable_pass_by_value' in attributes,
+ 'nullable_is_same_type': 'nullable_is_same_type' in attributes,
+ 'hashable': 'hashable' in attributes,
+ 'public_headers': values['public_headers'],
+ 'traits_headers': values['traits_headers'],
+ }
+ return result
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument(
+ '--dependency',
+ type=str,
+ action='append',
+ default=[],
+ help=('A path to another JSON typemap to merge into the output. '
+ 'This may be repeated to merge multiple typemaps.'))
+ parser.add_argument('--output',
+ type=str,
+ required=True,
+ help='The path to which to write the generated JSON.')
+ params, typemap_params = parser.parse_known_args()
+ typemaps = ParseTypemapArgs(typemap_params)
+ missing = [path for path in params.dependency if not os.path.exists(path)]
+ if missing:
+ raise IOError('Missing dependencies: %s' % ', '.join(missing))
+ for path in params.dependency:
+ typemaps.update(ReadTypemap(path))
+ with open(params.output, 'w') as f:
+ json.dump({'c++': typemaps}, f, indent=2)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
new file mode 100644
index 0000000000..f0d503e5e0
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
@@ -0,0 +1,131 @@
+{#---
+ Macro for enum definition, and the declaration of associated functions.
+---#}
+
+{%- macro enum_decl(enum) %}
+{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
+enum class {{enum_name}} : int32_t {
+{%- for field in enum.fields %}
+{%- if field.value %}
+ {{field.name}} = {{field.value|expression_to_text}},
+{%- else %}
+ {{field.name}},
+{%- endif %}
+{%- endfor %}
+};
+
+inline std::ostream& operator<<(std::ostream& os, {{enum_name}} value) {
+{%- if enum.fields %}
+ switch(value) {
+{%- for _, values in enum.fields|groupby('numeric_value') %}
+ case {{enum_name}}::{{values[0].name}}:
+ return os << "{{enum_name}}::
+{%- if values|length > 1 -%}
+ {{'{'}}
+{%- endif -%}
+ {{values|map(attribute='name')|join(', ')}}
+{%- if values|length > 1 -%}
+ {{'}'}}
+{%- endif -%}
+ ";
+{%- endfor %}
+ default:
+ return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value);
+ }
+{%- else %}
+ return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value);
+{%- endif %}
+}
+
+{#- Returns true if the given enum value exists in this version of enum. #}
+inline bool IsKnownEnumValue({{enum_name}} value) {
+ return {{enum|get_name_for_kind(internal=True,
+ flatten_nested_kind=True)}}::IsKnownValue(
+ static_cast<int32_t>(value));
+}
+{%- endmacro %}
+
+{%- macro enum_data_decl(enum) %}
+{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
+struct {{enum_name}}_Data {
+ public:
+ static bool constexpr kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
+
+ static bool IsKnownValue(int32_t value) {
+{%- if enum.fields %}
+ switch (value) {
+{%- for enum_field in enum.fields|groupby('numeric_value') %}
+ case {{enum_field[0]}}:
+{%- endfor %}
+ return true;
+ }
+{%- endif %}
+ return false;
+ }
+
+ static bool Validate(int32_t value,
+ mojo::internal::ValidationContext* validation_context) {
+ if (kIsExtensible || IsKnownValue(value))
+ return true;
+
+ ReportValidationError(validation_context,
+ mojo::internal::VALIDATION_ERROR_UNKNOWN_ENUM_VALUE);
+ return false;
+ }
+};
+{%- endmacro %}
+
+{%- macro enum_hash(enum) %}
+{%- set enum_name = enum|get_qualified_name_for_kind(
+ flatten_nested_kind=True) %}
+template <>
+struct hash<{{enum_name}}>
+ : public mojo::internal::EnumHashImpl<{{enum_name}}> {};
+{%- endmacro %}
+
+{%- macro enum_hash_blink(enum) %}
+{%- set enum_name = enum|get_qualified_name_for_kind(
+ flatten_nested_kind=True, include_variant=False) %}
+{%- set hash_fn_name = enum|wtf_hash_fn_name_for_enum %}
+{# We need two unused enum values: #}
+{%- set empty_value = -1000000 %}
+{%- set deleted_value = -1000001 %}
+{%- set empty_value_unused = "false" if empty_value in enum|all_enum_values else "true" %}
+{%- set deleted_value_unused = "false" if empty_value in enum|all_enum_values else "true" %}
+namespace WTF {
+struct {{hash_fn_name}} {
+ static unsigned hash(const {{enum_name}}& value) {
+ typedef base::underlying_type<{{enum_name}}>::type utype;
+ return DefaultHash<utype>::Hash().hash(static_cast<utype>(value));
+ }
+ static bool equal(const {{enum_name}}& left, const {{enum_name}}& right) {
+ return left == right;
+ }
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+template <>
+struct DefaultHash<{{enum_name}}> {
+ using Hash = {{hash_fn_name}};
+};
+
+template <>
+struct HashTraits<{{enum_name}}>
+ : public GenericHashTraits<{{enum_name}}> {
+ static_assert({{empty_value_unused}},
+ "{{empty_value}} is a reserved enum value");
+ static_assert({{deleted_value_unused}},
+ "{{deleted_value}} is a reserved enum value");
+ static const bool hasIsEmptyValueFunction = true;
+ static bool isEmptyValue(const {{enum_name}}& value) {
+ return value == static_cast<{{enum_name}}>({{empty_value}});
+ }
+ static void constructDeletedValue({{enum_name}}& slot, bool) {
+ slot = static_cast<{{enum_name}}>({{deleted_value}});
+ }
+ static bool isDeletedValue(const {{enum_name}}& value) {
+ return value == static_cast<{{enum_name}}>({{deleted_value}});
+ }
+};
+} // namespace WTF
+{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
new file mode 100644
index 0000000000..d7d0e5d873
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
@@ -0,0 +1,29 @@
+{%- set mojom_type = enum|get_qualified_name_for_kind(
+ flatten_nested_kind=True) %}
+
+template <>
+struct EnumTraits<{{mojom_type}}, {{mojom_type}}> {
+ static {{mojom_type}} ToMojom({{mojom_type}} input) { return input; }
+ static bool FromMojom({{mojom_type}} input, {{mojom_type}}* output) {
+ *output = input;
+ return true;
+ }
+};
+
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct Serializer<{{mojom_type}}, MaybeConstUserType> {
+ using UserType = typename std::remove_const<MaybeConstUserType>::type;
+ using Traits = EnumTraits<{{mojom_type}}, UserType>;
+
+ static void Serialize(UserType input, int32_t* output) {
+ *output = static_cast<int32_t>(Traits::ToMojom(input));
+ }
+
+ static bool Deserialize(int32_t input, UserType* output) {
+ return Traits::FromMojom(static_cast<{{mojom_type}}>(input), output);
+ }
+};
+
+} // namespace internal
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 0000000000..7f6497475a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -0,0 +1,65 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy;
+
+template <typename ImplRefTraits>
+class {{interface.name}}Stub;
+
+class {{interface.name}}RequestValidator;
+{%- if interface|has_callbacks %}
+class {{interface.name}}ResponseValidator;
+{%- endif %}
+
+class {{export_attribute}} {{interface.name}}
+ : public {{interface.name}}InterfaceBase {
+ public:
+ static const char Name_[];
+ static constexpr uint32_t Version_ = {{interface.version}};
+ static constexpr bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %};
+ static constexpr bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %};
+
+ using Proxy_ = {{interface.name}}Proxy;
+
+ template <typename ImplRefTraits>
+ using Stub_ = {{interface.name}}Stub<ImplRefTraits>;
+
+ using RequestValidator_ = {{interface.name}}RequestValidator;
+{%- if interface|has_callbacks %}
+ using ResponseValidator_ = {{interface.name}}ResponseValidator;
+{%- else %}
+ using ResponseValidator_ = mojo::PassThroughFilter;
+{%- endif %}
+
+{#--- Metadata #}
+ enum MethodMinVersions : uint32_t {
+{%- for method in interface.methods %}
+ k{{method.name}}MinVersion = {{method.min_version|default(0, true)}},
+{%- endfor %}
+ };
+
+{#--- Enums #}
+{%- for enum in interface.enums %}
+ using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}};
+{%- endfor %}
+
+{#--- Constants #}
+{%- for constant in interface.constants %}
+ static {{constant|format_constant_declaration(nested=True)}};
+{%- endfor %}
+
+{#--- Methods #}
+ virtual ~{{interface.name}}() {}
+
+{%- for method in interface.methods %}
+{% if method.response_parameters != None %}
+{%- if method.sync %}
+ // Sync method. This signature is used by the client side; the service side
+ // should implement the signature with callback below.
+ virtual bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}});
+{%- endif %}
+
+ using {{method.name}}Callback = {{interface_macros.declare_callback(method,
+ for_blink, use_once_callback)}};
+{%- endif %}
+ virtual void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) = 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 0000000000..aba18380af
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -0,0 +1,448 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+{%- import "struct_macros.tmpl" as struct_macros %}
+
+{%- set class_name = interface.name %}
+{%- set proxy_name = interface.name ~ "Proxy" %}
+{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
+
+{%- macro alloc_params(struct, params, message, description) %}
+ mojo::internal::SerializationContext serialization_context;
+ serialization_context.handles.Swap(({{message}})->mutable_handles());
+ serialization_context.associated_endpoint_handles.swap(
+ *({{message}})->mutable_associated_endpoint_handles());
+ bool success = true;
+{%- for param in struct.packed.packed_fields_in_ordinal_order %}
+ {{param.field.kind|cpp_wrapper_type}} p_{{param.field.name}}{};
+{%- endfor %}
+ {{struct.name}}DataView input_data_view({{params}}, &serialization_context);
+ {{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}}
+ if (!success) {
+ ReportValidationErrorForMessage(
+ {{message}},
+ mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
+ "{{description}} deserializer");
+ return false;
+ }
+{%- endmacro %}
+
+{%- macro pass_params(parameters) %}
+{%- for param in parameters %}
+std::move(p_{{param.name}})
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro build_message(struct, input_pattern, struct_display_name,
+ serialization_context) -%}
+ {{struct_macros.serialize(struct, struct_display_name, input_pattern,
+ "params", "builder.buffer()",
+ serialization_context)}}
+ ({{serialization_context}})->handles.Swap(
+ builder.message()->mutable_handles());
+ ({{serialization_context}})->associated_endpoint_handles.swap(
+ *builder.message()->mutable_associated_endpoint_handles());
+{%- endmacro %}
+
+{#--- Begin #}
+const char {{class_name}}::Name_[] = "{{namespace_as_string}}::{{class_name}}";
+
+{#--- Constants #}
+{%- for constant in interface.constants %}
+{%- if constant.kind|is_string_kind %}
+const char {{interface.name}}::{{constant.name}}[] = {{constant|constant_value}};
+{%- endif %}
+{%- endfor %}
+
+
+{%- for method in interface.methods %}
+{%- if method.sync %}
+bool {{class_name}}::{{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) {
+ NOTREACHED();
+ return false;
+}
+{%- endif %}
+{%- endfor %}
+
+{#--- ForwardToCallback definition #}
+{%- for method in interface.methods -%}
+{%- if method.response_parameters != None %}
+{%- if method.sync %}
+class {{class_name}}_{{method.name}}_HandleSyncResponse
+ : public mojo::MessageReceiver {
+ public:
+ {{class_name}}_{{method.name}}_HandleSyncResponse(
+ bool* result
+{%- for param in method.response_parameters -%}
+ , {{param.kind|cpp_wrapper_type}}* out_{{param.name}}
+{%- endfor %})
+ : result_(result)
+{%- for param in method.response_parameters -%}
+ , out_{{param.name}}_(out_{{param.name}})
+{%- endfor %} {
+ DCHECK(!*result_);
+ }
+ bool Accept(mojo::Message* message) override;
+ private:
+ bool* result_;
+{%- for param in method.response_parameters %}
+ {{param.kind|cpp_wrapper_type}}* out_{{param.name}}_;
+{%- endfor -%}
+ DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_HandleSyncResponse);
+};
+bool {{class_name}}_{{method.name}}_HandleSyncResponse::Accept(
+ mojo::Message* message) {
+ internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
+ message->mutable_payload());
+
+{%- set desc = class_name~"::"~method.name~" response" %}
+ {{alloc_params(method.response_param_struct, "params", "message", desc)}}
+
+{%- for param in method.response_parameters %}
+ *out_{{param.name}}_ = std::move(p_{{param.name}});
+{%- endfor %}
+ mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage(
+ message);
+ *result_ = true;
+ return true;
+}
+{%- endif %}
+
+class {{class_name}}_{{method.name}}_ForwardToCallback
+ : public mojo::MessageReceiver {
+ public:
+ {{class_name}}_{{method.name}}_ForwardToCallback(
+{%- if use_once_callback %}
+ {{class_name}}::{{method.name}}Callback callback
+{%- else %}
+ const {{class_name}}::{{method.name}}Callback& callback
+{%- endif %}
+ ) : callback_(std::move(callback)) {
+ }
+ bool Accept(mojo::Message* message) override;
+ private:
+ {{class_name}}::{{method.name}}Callback callback_;
+ 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());
+
+{%- set desc = class_name~"::"~method.name~" response" %}
+ {{alloc_params(method.response_param_struct, "params", "message", desc)}}
+ if (!callback_.is_null()) {
+ mojo::internal::MessageDispatchContext context(message);
+ std::move(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_struct = method.param_struct %}
+{%- set params_description =
+ "%s.%s request"|format(interface.name, method.name) %}
+{%- if method.sync %}
+bool {{proxy_name}}::{{method.name}}(
+ {{interface_macros.declare_sync_method_params("param_", method)}}) {
+ mojo::internal::SerializationContext serialization_context;
+ {{struct_macros.get_serialized_size(params_struct, "param_%s",
+ "&serialization_context")}}
+
+ mojo::internal::MessageBuilder builder(
+ {{message_name}},
+ mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse,
+ size, serialization_context.associated_endpoint_count);
+
+ {{build_message(params_struct, "param_%s", params_description,
+ "&serialization_context")}}
+
+ bool result = false;
+ std::unique_ptr<mojo::MessageReceiver> responder(
+ new {{class_name}}_{{method.name}}_HandleSyncResponse(
+ &result
+{%- for param in method.response_parameters -%}
+ , param_{{param.name}}
+{%- endfor %}));
+ ignore_result(receiver_->AcceptWithResponder(builder.message(),
+ std::move(responder)));
+ return result;
+}
+{%- endif %}
+
+void {{proxy_name}}::{{method.name}}(
+ {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) {
+ mojo::internal::SerializationContext serialization_context;
+ {{struct_macros.get_serialized_size(params_struct, "in_%s",
+ "&serialization_context")}}
+
+{%- if method.response_parameters != None %}
+ constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse;
+{%- else %}
+ constexpr uint32_t kFlags = 0;
+{%- endif %}
+ mojo::internal::MessageBuilder builder(
+ {{message_name}}, kFlags, size,
+ serialization_context.associated_endpoint_count);
+
+ {{build_message(params_struct, "in_%s", params_description,
+ "&serialization_context")}}
+
+{%- if method.response_parameters != None %}
+ std::unique_ptr<mojo::MessageReceiver> responder(
+ new {{class_name}}_{{method.name}}_ForwardToCallback(
+ std::move(callback)));
+ ignore_result(receiver_->AcceptWithResponder(builder.message(),
+ std::move(responder)));
+{%- else %}
+ // This return value may be ignored as false implies the Connector has
+ // encountered an error, which will be visible through other means.
+ ignore_result(receiver_->Accept(builder.message()));
+{%- 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 response_params_struct = method.response_param_struct %}
+{%- set params_description =
+ "%s.%s response"|format(interface.name, method.name) %}
+class {{class_name}}_{{method.name}}_ProxyToResponder {
+ public:
+ static {{class_name}}::{{method.name}}Callback CreateCallback(
+ uint64_t request_id,
+ bool is_sync,
+ std::unique_ptr<mojo::MessageReceiverWithStatus> responder) {
+ std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy(
+ new {{class_name}}_{{method.name}}_ProxyToResponder(
+ request_id, is_sync, std::move(responder)));
+ return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run,
+ base::Passed(&proxy));
+ }
+
+ ~{{class_name}}_{{method.name}}_ProxyToResponder() {
+#if DCHECK_IS_ON()
+ if (responder_) {
+ // Is the Service destroying the callback without running it
+ // and without first closing the pipe?
+ responder_->DCheckInvalid("The callback passed to "
+ "{{class_name}}::{{method.name}}() was never run.");
+ }
+#endif
+ // If the Callback was dropped then deleting the responder will close
+ // the pipe so the calling application knows to stop waiting for a reply.
+ responder_ = nullptr;
+ }
+
+ private:
+ {{class_name}}_{{method.name}}_ProxyToResponder(
+ uint64_t request_id,
+ bool is_sync,
+ std::unique_ptr<mojo::MessageReceiverWithStatus> responder)
+ : request_id_(request_id),
+ is_sync_(is_sync),
+ responder_(std::move(responder)) {
+ }
+
+ void Run(
+ {{interface_macros.declare_responder_params(
+ "in_", method.response_parameters, for_blink)}});
+
+ uint64_t request_id_;
+ bool is_sync_;
+ std::unique_ptr<mojo::MessageReceiverWithStatus> responder_;
+
+ DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
+};
+
+void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
+ {{interface_macros.declare_responder_params(
+ "in_", method.response_parameters, for_blink)}}) {
+ mojo::internal::SerializationContext serialization_context;
+ {{struct_macros.get_serialized_size(response_params_struct, "in_%s",
+ "&serialization_context")}}
+
+ uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) |
+ mojo::Message::kFlagIsResponse;
+ mojo::internal::MessageBuilder builder(
+ {{message_name}}, flags, size,
+ serialization_context.associated_endpoint_count);
+ builder.message()->set_request_id(request_id_);
+
+ {{build_message(response_params_struct, "in_%s", params_description,
+ "&serialization_context")}}
+ ignore_result(responder_->Accept(builder.message()));
+ // TODO(darin): Accept() returning false 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.
+ responder_ = nullptr;
+}
+{%- endif -%}
+{%- endfor %}
+
+{#--- StubDispatch definition #}
+
+// static
+bool {{class_name}}StubDispatch::Accept(
+ {{interface.name}}* impl,
+ 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());
+
+{%- set desc = class_name~"::"~method.name %}
+ {{alloc_params(method.param_struct, "params", "message", desc)|
+ indent(4)}}
+ // A null |impl| means no implementation was bound.
+ assert(impl);
+ TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}");
+ mojo::internal::MessageDispatchContext context(message);
+ impl->{{method.name}}({{pass_params(method.parameters)}});
+ return true;
+{%- else %}
+ break;
+{%- endif %}
+ }
+{%- endfor %}
+ }
+{%- endif %}
+ return false;
+}
+
+// static
+bool {{class_name}}StubDispatch::AcceptWithResponder(
+ {{interface.name}}* impl,
+ mojo::Message* message,
+ std::unique_ptr<mojo::MessageReceiverWithStatus> 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());
+
+{%- set desc = class_name~"::"~method.name %}
+ {{alloc_params(method.param_struct, "params", "message", desc)|
+ indent(4)}}
+ {{class_name}}::{{method.name}}Callback callback =
+ {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback(
+ message->request_id(),
+ message->has_flag(mojo::Message::kFlagIsSync),
+ std::move(responder));
+ // A null |impl| means no implementation was bound.
+ assert(impl);
+ TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}");
+ mojo::internal::MessageDispatchContext context(message);
+ impl->{{method.name}}(
+{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}std::move(callback));
+ return true;
+{%- else %}
+ break;
+{%- endif %}
+ }
+{%- endfor %}
+ }
+{%- endif %}
+ return false;
+}
+
+{#--- Request validator definitions #}
+
+bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
+ if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
+ return true;
+
+ mojo::internal::ValidationContext validation_context(
+ message->payload(), message->payload_num_bytes(),
+ message->handles()->size(), message->payload_num_interface_ids(), message,
+ "{{class_name}} RequestValidator");
+
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters != None %}
+ if (!mojo::internal::ValidateMessageIsRequestExpectingResponse(
+ message, &validation_context)) {
+ return false;
+ }
+{%- else %}
+ if (!mojo::internal::ValidateMessageIsRequestWithoutResponse(
+ message, &validation_context)) {
+ return false;
+ }
+{%- endif %}
+ if (!mojo::internal::ValidateMessagePayload<
+ internal::{{class_name}}_{{method.name}}_Params_Data>(
+ message, &validation_context)) {
+ return false;
+ }
+ return true;
+ }
+{%- endfor %}
+ default:
+ break;
+ }
+
+ // Unrecognized message.
+ ReportValidationError(
+ &validation_context,
+ mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
+ return false;
+}
+
+{#--- Response validator definitions #}
+{% if interface|has_callbacks %}
+bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
+ if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
+ return true;
+
+ mojo::internal::ValidationContext validation_context(
+ message->payload(), message->payload_num_bytes(),
+ message->handles()->size(), message->payload_num_interface_ids(), message,
+ "{{class_name}} ResponseValidator");
+
+ if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context))
+ return false;
+ switch (message->header()->name) {
+{%- for method in interface.methods if method.response_parameters != None %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+ if (!mojo::internal::ValidateMessagePayload<
+ internal::{{class_name}}_{{method.name}}_ResponseParams_Data>(
+ message, &validation_context)) {
+ return false;
+ }
+ return true;
+ }
+{%- endfor %}
+ default:
+ break;
+ }
+
+ // Unrecognized message.
+ ReportValidationError(
+ &validation_context,
+ mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
+ return false;
+}
+{%- 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 0000000000..8649273633
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -0,0 +1,49 @@
+{%- macro declare_params(prefix, parameters) %}
+{%- for param in parameters -%}
+{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro declare_responder_params(prefix, parameters, for_blink) %}
+{%- for param in parameters -%}
+{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro declare_callback(method, for_blink, use_once_callback) -%}
+{%- if use_once_callback -%}
+base::OnceCallback<void(
+{%- else -%}
+base::Callback<void(
+{%- endif -%}
+{%- for param in method.response_parameters -%}
+{{param.kind|cpp_wrapper_param_type}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor -%}
+)>
+{%- endmacro -%}
+
+{%- macro declare_request_params(prefix, method, use_once_callback) -%}
+{{declare_params(prefix, method.parameters)}}
+{%- if method.response_parameters != None -%}
+{%- if method.parameters %}, {% endif -%}
+{%- if use_once_callback -%}
+{{method.name}}Callback callback
+{%- else -%}
+const {{method.name}}Callback& callback
+{%- endif -%}
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro declare_sync_method_params(prefix, method) -%}
+{{declare_params(prefix, method.parameters)}}
+{%- if method.response_parameters %}
+{%- if method.parameters %}, {% endif %}
+{%- for param in method.response_parameters -%}
+{{param.kind|cpp_wrapper_type}}* {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- 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 0000000000..0a158ec3e8
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -0,0 +1,16 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{export_attribute}} {{interface.name}}Proxy
+ : public {{interface.name}} {
+ public:
+ explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
+
+{%- for method in interface.methods %}
+{%- if method.sync %}
+ bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) override;
+{%- endif %}
+ void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) 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 0000000000..a00d14886d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
@@ -0,0 +1,4 @@
+class {{export_attribute}} {{interface.name}}RequestValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) {
+ public:
+ 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 0000000000..e2caa02c79
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
@@ -0,0 +1,4 @@
+class {{export_attribute}} {{interface.name}}ResponseValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) {
+ public:
+ 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 0000000000..79ab46f337
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -0,0 +1,41 @@
+class {{export_attribute}} {{interface.name}}StubDispatch {
+ public:
+ static bool Accept({{interface.name}}* impl, mojo::Message* message);
+ static bool AcceptWithResponder(
+ {{interface.name}}* impl,
+ mojo::Message* message,
+ std::unique_ptr<mojo::MessageReceiverWithStatus> responder);
+};
+
+template <typename ImplRefTraits =
+ mojo::RawPtrImplRefTraits<{{interface.name}}>>
+class {{interface.name}}Stub
+ : public NON_EXPORTED_BASE(mojo::MessageReceiverWithResponderStatus) {
+ public:
+ using ImplPointerType = typename ImplRefTraits::PointerType;
+
+ {{interface.name}}Stub() {}
+ ~{{interface.name}}Stub() override {}
+
+ void set_sink(ImplPointerType sink) { sink_ = std::move(sink); }
+ ImplPointerType& sink() { return sink_; }
+
+ bool Accept(mojo::Message* message) override {
+ if (ImplRefTraits::IsNull(sink_))
+ return false;
+ return {{interface.name}}StubDispatch::Accept(
+ ImplRefTraits::GetRawPointer(&sink_), message);
+ }
+
+ bool AcceptWithResponder(
+ mojo::Message* message,
+ std::unique_ptr<mojo::MessageReceiverWithStatus> responder) override {
+ if (ImplRefTraits::IsNull(sink_))
+ return false;
+ return {{interface.name}}StubDispatch::AcceptWithResponder(
+ ImplRefTraits::GetRawPointer(&sink_), message, std::move(responder));
+ }
+
+ private:
+ ImplPointerType sink_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
new file mode 100644
index 0000000000..964b25438e
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
@@ -0,0 +1,96 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{%- set header_guard = "%s_SHARED_INTERNAL_H_"|format(
+ module.path|upper|replace("/","_")|replace(".","_")|
+ replace("-", "_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/native_enum_data.h"
+#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+
+{%- for import in imports %}
+#include "{{import.module.path}}-shared-internal.h"
+{%- endfor %}
+
+namespace mojo {
+namespace internal {
+class ValidationContext;
+}
+}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+namespace internal {
+
+{#--- Internal forward declarations #}
+{%- for struct in structs %}
+{%- if struct|is_native_only_kind %}
+using {{struct.name}}_Data = mojo::internal::NativeStruct_Data;
+{%- else %}
+class {{struct.name}}_Data;
+{%- endif %}
+{%- endfor %}
+
+{%- for union in unions %}
+class {{union.name}}_Data;
+{%- endfor %}
+
+{#--- Enums #}
+{%- from "enum_macros.tmpl" import enum_data_decl -%}
+{%- for enum in all_enums %}
+{%- if enum|is_native_only_kind %}
+using {{enum|get_name_for_kind(flatten_nested_kind=True)}}_Data =
+ mojo::internal::NativeEnum_Data;
+{%- else %}
+{{enum_data_decl(enum)}}
+{%- endif %}
+{%- endfor %}
+
+#pragma pack(push, 1)
+
+{#--- Unions must be declared first because they can be members of structs #}
+{#--- Union class declarations #}
+{%- for union in unions %}
+{% include "union_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Struct class declarations #}
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
+constexpr uint32_t {{method_name}} = {{method.ordinal}};
+{%- set struct = method.param_struct %}
+{% include "struct_declaration.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method.response_param_struct %}
+{% include "struct_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- 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-shared.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl
new file mode 100644
index 0000000000..645bb692b0
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4065)
+#endif
+
+#include "{{module.path}}-shared.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+
+{%- for header in extra_traits_headers %}
+#include "{{header}}"
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+namespace internal {
+
+{#--- Union definitions #}
+{%- for union in unions %}
+{% include "union_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Struct definitions #}
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
+{%- set struct = method.param_struct %}
+{% include "struct_definition.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method.response_param_struct %}
+{% include "struct_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+} // namespace internal
+
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
new file mode 100644
index 0000000000..dd13466de1
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
@@ -0,0 +1,212 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{%- set header_guard = "%s_SHARED_H_"|format(
+ module.path|upper|replace("/","_")|replace(".","_")|
+ replace("-", "_")) %}
+
+{%- macro mojom_type_traits(kind) %}
+template <>
+struct MojomTypeTraits<{{kind|get_qualified_name_for_kind}}DataView> {
+ using Data = {{kind|get_qualified_name_for_kind(internal=True)}};
+{%- if kind|is_union_kind %}
+ using DataAsArrayElement = Data;
+ static constexpr MojomTypeCategory category = MojomTypeCategory::UNION;
+{%- else %}
+ using DataAsArrayElement = Pointer<Data>;
+ static constexpr MojomTypeCategory category = MojomTypeCategory::STRUCT;
+{%- endif %}
+};
+{%- endmacro %}
+
+{%- macro namespace_begin() %}
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro namespace_end() %}
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+{%- endmacro %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include <stdint.h>
+
+#include <functional>
+#include <ostream>
+#include <type_traits>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "mojo/public/cpp/bindings/array_data_view.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/interface_data_view.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/map_data_view.h"
+#include "mojo/public/cpp/bindings/native_enum.h"
+#include "mojo/public/cpp/bindings/native_struct_data_view.h"
+#include "mojo/public/cpp/bindings/string_data_view.h"
+#include "{{module.path}}-shared-internal.h"
+{%- for import in imports %}
+#include "{{import.module.path}}-shared.h"
+{%- endfor %}
+
+{{namespace_begin()}}
+
+{#--- Struct Forward Declarations -#}
+{%- for struct in structs %}
+{%- if struct|is_native_only_kind %}
+using {{struct.name}}DataView = mojo::NativeStructDataView;
+{%- else %}
+class {{struct.name}}DataView;
+{%- endif %}
+{% endfor %}
+
+{#--- Union Forward Declarations -#}
+{%- for union in unions %}
+class {{union.name}}DataView;
+{%- endfor %}
+
+{{namespace_end()}}
+
+namespace mojo {
+namespace internal {
+
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{{mojom_type_traits(struct)}}
+{%- endif %}
+{%- endfor %}
+
+{%- for union in unions %}
+{{mojom_type_traits(union)}}
+{%- endfor %}
+
+} // namespace internal
+} // namespace mojo
+
+{{namespace_begin()}}
+
+{#--- Enums #}
+{%- from "enum_macros.tmpl" import enum_decl%}
+{%- for enum in all_enums %}
+{%- if enum|is_native_only_kind %}
+using {{enum|get_name_for_kind(flatten_nested_kind=True)}} = mojo::NativeEnum;
+{%- else %}
+{{enum_decl(enum)}}
+{%- endif %}
+{%- endfor %}
+
+{#--- Interfaces #}
+{%- if interfaces %}
+// Interface base classes. They are used for type safety check.
+{%- endif %}
+{%- for interface in interfaces %}
+class {{interface.name}}InterfaceBase {};
+
+using {{interface.name}}PtrDataView =
+ mojo::InterfacePtrDataView<{{interface.name}}InterfaceBase>;
+using {{interface.name}}RequestDataView =
+ mojo::InterfaceRequestDataView<{{interface.name}}InterfaceBase>;
+using {{interface.name}}AssociatedPtrInfoDataView =
+ mojo::AssociatedInterfacePtrInfoDataView<{{interface.name}}InterfaceBase>;
+using {{interface.name}}AssociatedRequestDataView =
+ mojo::AssociatedInterfaceRequestDataView<{{interface.name}}InterfaceBase>;
+
+{%- endfor %}
+
+{#--- Structs #}
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_data_view_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set struct = method.param_struct %}
+{% include "struct_data_view_declaration.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method.response_param_struct %}
+{% include "struct_data_view_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+{#--- Unions #}
+{%- for union in unions %}
+{% include "union_data_view_declaration.tmpl" %}
+{%- endfor %}
+
+{{namespace_end()}}
+
+namespace std {
+
+{%- from "enum_macros.tmpl" import enum_hash %}
+{%- for enum in all_enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%- endif %}
+{%- endfor %}
+
+} // namespace std
+
+namespace mojo {
+
+{#--- Enum Serialization Helpers -#}
+{%- for enum in all_enums %}
+{%- if not enum|is_native_only_kind %}
+{% include "enum_serialization_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers -#}
+{% for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_serialization_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Union Serialization Helpers -#}
+{% if unions %}
+{%- for union in unions %}
+{% include "union_serialization_declaration.tmpl" %}
+{%- endfor %}
+{%- endif %}
+
+} // namespace mojo
+
+{{namespace_begin()}}
+
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_data_view_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set struct = method.param_struct %}
+{% include "struct_data_view_definition.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method.response_param_struct %}
+{% include "struct_data_view_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+{%- for union in unions %}
+{% include "union_data_view_definition.tmpl" %}
+{%- endfor %}
+
+{{namespace_end()}}
+
+#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 0000000000..2c66a85f87
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -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.
+
+{%- if variant -%}
+{%- set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%- set variant_path = module.path -%}
+{%- endif %}
+
+#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:4065)
+#pragma warning(disable:4756)
+#endif
+
+#include "{{variant_path}}.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/lib/validate_params.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
+
+{%- if for_blink %}
+#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
+{%- endif %}
+
+{%- for header in extra_traits_headers %}
+#include "{{header}}"
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
+
+{#--- Constants #}
+{%- for constant in module.constants %}
+{%- if constant.kind|is_string_kind %}
+const char {{constant.name}}[] = {{constant|constant_value}};
+{%- endif %}
+{%- endfor %}
+
+{#--- Struct Constants #}
+{%- for struct in structs %}
+{%- for constant in struct.constants %}
+{%- if constant.kind|is_string_kind %}
+const char {{struct.name}}::{{constant.name}}[] = {{constant|constant_value}};
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+{#--- Struct builder definitions #}
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{%- include "wrapper_class_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Union builder definitions #}
+{%- for union in unions %}
+{%- include "wrapper_union_class_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces %}
+{%- include "interface_definition.tmpl" %}
+{%- endfor %}
+
+{%- if variant %}
+} // namespace {{variant}}
+{%- endif %}
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+namespace mojo {
+
+{#--- Struct Serialization Helpers -#}
+{% for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_traits_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Union Serialization Helpers #}
+{%- for union in unions %}
+{%- include "union_traits_definition.tmpl" %}
+{%- endfor %}
+
+} // namespace mojo
+
+#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 0000000000..804a46b6f8
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -0,0 +1,236 @@
+// 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 variant -%}
+{%- set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%- set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_H_"|format(
+ variant_path|upper|replace("/","_")|replace(".","_")|
+ replace("-", "_")) %}
+
+{%- macro namespace_begin() %}
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
+{%- endmacro %}
+
+{%- macro namespace_end() %}
+{%- if variant %}
+} // namespace {{variant}}
+{%- endif %}
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+{%- endmacro %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include <stdint.h>
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/clone_traits.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/equals_traits.h"
+#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/union_accessor.h"
+#include "mojo/public/cpp/bindings/native_struct.h"
+#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
+#include "mojo/public/cpp/bindings/union_traits.h"
+#include "{{module.path}}-shared.h"
+{%- for import in imports %}
+{%- if variant %}
+#include "{{"%s-%s.h"|format(import.module.path, variant)}}"
+{%- else %}
+#include "{{import.module.path}}.h"
+{%- endif %}
+{%- endfor %}
+{%- if not for_blink %}
+#include <string>
+#include <vector>
+{%- else %}
+{# hash_util.h includes template specializations that should be present for
+ every use of {Inlined}StructPtr. #}
+#include "mojo/public/cpp/bindings/lib/wtf_hash_util.h"
+#include "third_party/WebKit/Source/wtf/HashFunctions.h"
+#include "third_party/WebKit/Source/wtf/Optional.h"
+#include "third_party/WebKit/Source/wtf/text/WTFString.h"
+{%- endif %}
+
+{%- for header in extra_public_headers %}
+#include "{{header}}"
+{%- endfor %}
+
+{%- if export_header %}
+#include "{{export_header}}"
+{%- endif %}
+
+{#--- WTF enum hashing #}
+{%- from "enum_macros.tmpl" import enum_hash_blink%}
+{%- if for_blink %}
+{%- for enum in all_enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_hash_blink(enum)}}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+
+{{namespace_begin()}}
+
+{#--- Enums #}
+{%- if variant %}
+{%- for enum in enums %}
+using {{enum.name}} = {{enum.name}}; // Alias for definition in the parent namespace.
+{%- endfor %}
+{%- endif %}
+
+{#--- Constants #}
+{%- for constant in module.constants %}
+{{constant|format_constant_declaration}};
+{%- endfor %}
+
+{#--- Interface Forward Declarations -#}
+{% for interface in interfaces %}
+class {{interface.name}};
+using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
+using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
+using ThreadSafe{{interface.name}}Ptr =
+ mojo::ThreadSafeInterfacePtr<{{interface.name}}>;
+using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
+using {{interface.name}}AssociatedPtr =
+ mojo::AssociatedInterfacePtr<{{interface.name}}>;
+using ThreadSafe{{interface.name}}AssociatedPtr =
+ mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>;
+using {{interface.name}}AssociatedPtrInfo =
+ mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
+using {{interface.name}}AssociatedRequest =
+ mojo::AssociatedInterfaceRequest<{{interface.name}}>;
+{% endfor %}
+
+{#--- Struct Forward Declarations -#}
+{% for struct in structs %}
+{%- if struct|is_native_only_kind %}
+using {{struct.name}} = mojo::NativeStruct;
+using {{struct.name}}Ptr = mojo::NativeStructPtr;
+{%- else %}
+class {{struct.name}};
+{%- if struct|should_inline %}
+using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>;
+{%- else %}
+using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>;
+{%- endif %}
+{%- endif %}
+{% endfor %}
+
+{#--- Union Forward Declarations -#}
+{% for union in unions %}
+class {{union.name}};
+{% if union|should_inline_union %}
+typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr;
+{% else %}
+typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr;
+{% 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 %}
+
+{#--- NOTE: Unions and 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 and not struct|is_native_only_kind %}
+{% include "wrapper_class_declaration.tmpl" %}
+{% endif %}
+{%- endfor %}
+
+{#--- Unions must be declared before non-inlined structs because they can be
+ members of structs. #}
+{#--- Unions #}
+{% for union in unions %}
+{% include "wrapper_union_class_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Non-inlined structs #}
+{% for struct in structs %}
+{% if not struct|should_inline and not struct|is_native_only_kind %}
+{% include "wrapper_class_declaration.tmpl" %}
+{% endif %}
+{%- endfor %}
+
+{%- for union in unions %}
+{% include "wrapper_union_class_template_definition.tmpl" %}
+{%- endfor %}
+
+{%- for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "wrapper_class_template_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{{namespace_end()}}
+
+namespace mojo {
+
+{#--- Struct Serialization Helpers -#}
+{% for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_traits_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Union Serialization Helpers -#}
+{% if unions %}
+{%- for union in unions %}
+{% include "union_traits_declaration.tmpl" %}
+{%- endfor %}
+{%- endif %}
+
+} // namespace mojo
+
+#endif // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
new file mode 100644
index 0000000000..96e0d614d8
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
@@ -0,0 +1,118 @@
+class {{struct.name}}DataView {
+ public:
+ {{struct.name}}DataView() {}
+
+ {{struct.name}}DataView(
+ internal::{{struct.name}}_Data* data,
+ mojo::internal::SerializationContext* context)
+{%- if struct|requires_context_for_data_view %}
+ : data_(data), context_(context) {}
+{%- else %}
+ : data_(data) {}
+{%- endif %}
+
+ bool is_null() const { return !data_; }
+
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%- set kind = pf.field.kind %}
+{%- set name = pf.field.name %}
+{%- if kind|is_union_kind %}
+ inline void Get{{name|under_to_camel}}DataView(
+ {{kind|cpp_data_view_type}}* output);
+
+ template <typename UserType>
+ WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
+{%- if pf.min_version != 0 %}
+ auto* pointer = data_->header_.version >= {{pf.min_version}}
+ ? &data_->{{name}} : nullptr;
+{%- else %}
+ auto* pointer = &data_->{{name}};
+{%- endif %}
+ return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ pointer, output, context_);
+ }
+
+{%- elif kind|is_object_kind %}
+ inline void Get{{name|under_to_camel}}DataView(
+ {{kind|cpp_data_view_type}}* output);
+
+ template <typename UserType>
+ WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
+{%- if pf.min_version != 0 %}
+ auto* pointer = data_->header_.version >= {{pf.min_version}}
+ ? data_->{{name}}.Get() : nullptr;
+{%- else %}
+ auto* pointer = data_->{{name}}.Get();
+{%- endif %}
+ return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ pointer, output, context_);
+ }
+
+{%- elif kind|is_enum_kind %}
+ template <typename UserType>
+ WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const {
+{%- if pf.min_version != 0 %}
+ auto data_value = data_->header_.version >= {{pf.min_version}}
+ ? data_->{{name}} : 0;
+{%- else %}
+ auto data_value = data_->{{name}};
+{%- endif %}
+ return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ data_value, output);
+ }
+
+ {{kind|cpp_data_view_type}} {{name}}() const {
+{%- if pf.min_version != 0 %}
+ if (data_->header_.version < {{pf.min_version}})
+ return {{kind|get_qualified_name_for_kind}}{};
+{%- endif %}
+ return static_cast<{{kind|cpp_data_view_type}}>(data_->{{name}});
+ }
+
+{%- elif kind|is_any_handle_kind %}
+ {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() {
+ {{kind|cpp_data_view_type}} result;
+{%- if pf.min_version != 0 %}
+ if (data_->header_.version < {{pf.min_version}})
+ return result;
+{%- endif %}
+ bool ret =
+ mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ &data_->{{name}}, &result, context_);
+ DCHECK(ret);
+ return result;
+ }
+
+{%- elif kind|is_any_interface_kind %}
+ template <typename UserType>
+ UserType Take{{name|under_to_camel}}() {
+ UserType result;
+{%- if pf.min_version != 0 %}
+ if (data_->header_.version < {{pf.min_version}})
+ return result;
+{%- endif %}
+ bool ret =
+ mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ &data_->{{name}}, &result, context_);
+ DCHECK(ret);
+ return result;
+ }
+
+{%- else %}
+ {{kind|cpp_data_view_type}} {{name}}() const {
+{%- if pf.min_version != 0 %}
+ if (data_->header_.version < {{pf.min_version}})
+ return {{kind|cpp_data_view_type}}{};
+{%- endif %}
+ return data_->{{name}};
+ }
+
+{%- endif %}
+{%- endfor %}
+ private:
+ internal::{{struct.name}}_Data* data_ = nullptr;
+{%- if struct|requires_context_for_data_view %}
+ mojo::internal::SerializationContext* context_ = nullptr;
+{%- endif %}
+};
+
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
new file mode 100644
index 0000000000..95311dc124
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
@@ -0,0 +1,30 @@
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%- set kind = pf.field.kind %}
+{%- set name = pf.field.name %}
+
+{%- if kind|is_union_kind %}
+inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView(
+ {{kind|cpp_data_view_type}}* output) {
+{%- if pf.min_version != 0 %}
+ auto pointer = data_->header_.version >= {{pf.min_version}}
+ ? &data_->{{name}} : nullptr;
+{%- else %}
+ auto pointer = &data_->{{name}};
+{%- endif %}
+ *output = {{kind|cpp_data_view_type}}(pointer, context_);
+}
+
+{%- elif kind|is_object_kind %}
+inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView(
+ {{kind|cpp_data_view_type}}* output) {
+{%- if pf.min_version != 0 %}
+ auto pointer = data_->header_.version >= {{pf.min_version}}
+ ? data_->{{name}}.Get() : nullptr;
+{%- else %}
+ auto pointer = data_->{{name}}.Get();
+{%- endif %}
+ *output = {{kind|cpp_data_view_type}}(pointer, context_);
+}
+{%- endif %}
+{%- endfor %}
+
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 0000000000..156f7742c4
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -0,0 +1,46 @@
+{%- 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::ValidationContext* validation_context);
+
+ mojo::internal::StructHeader header_;
+{%- for packed_field in struct.packed.packed_fields %}
+{%- set name = packed_field.field.name %}
+{%- set kind = packed_field.field.kind %}
+{%- if kind.spec == 'b' %}
+ uint8_t {{name}} : 1;
+{%- else %}
+ {{kind|cpp_field_type}} {{name}};
+{%- endif %}
+{%- 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.versions[-1].num_fields %}
+{%- 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 %}
+
+ private:
+ {{class_name}}() : header_({sizeof(*this), {{struct.versions[-1].version}}}) {
+ }
+ ~{{class_name}}() = delete;
+};
+static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}},
+ "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 0000000000..60dca4010e
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -0,0 +1,70 @@
+{%- import "validation_macros.tmpl" as validation_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+
+// static
+bool {{class_name}}::Validate(
+ const void* data,
+ mojo::internal::ValidationContext* validation_context) {
+ if (!data)
+ return true;
+
+ if (!ValidateStructHeaderAndClaimMemory(data, validation_context))
+ return false;
+
+ // NOTE: The memory backing |object| may be smaller than |sizeof(*object)| if
+ // the message comes from an older version.
+ const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
+
+ static constexpr struct {
+ uint32_t version;
+ uint32_t num_bytes;
+ } kVersionSizes[] = {
+{%- for version in struct.versions -%}
+ { {{version.version}}, {{version.num_bytes}} }{% if not loop.last %}, {% endif -%}
+{%- endfor -%}
+ };
+
+ if (object->header_.version <=
+ kVersionSizes[arraysize(kVersionSizes) - 1].version) {
+ // Scan in reverse order to optimize for more recent versions.
+ for (int i = arraysize(kVersionSizes) - 1; i >= 0; --i) {
+ if (object->header_.version >= kVersionSizes[i].version) {
+ if (object->header_.num_bytes == kVersionSizes[i].num_bytes)
+ break;
+
+ ReportValidationError(
+ validation_context,
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ }
+ } else if (object->header_.num_bytes <
+ kVersionSizes[arraysize(kVersionSizes) - 1].num_bytes) {
+ ReportValidationError(
+ validation_context,
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+
+{#- Before validating fields introduced at a certain version, we need to add
+ a version check, which makes sure we skip further validation if |object|
+ is from an earlier version. |last_checked_version| records the last
+ version that we have added such version check. #}
+{%- set last_checked_version = 0 %}
+{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
+{%- set kind = packed_field.field.kind %}
+{%- if kind|is_object_kind or kind|is_any_handle_or_interface_kind or
+ kind|is_enum_kind %}
+{%- if packed_field.min_version > last_checked_version %}
+{%- set last_checked_version = packed_field.min_version %}
+ if (object->header_.version < {{packed_field.min_version}})
+ return true;
+{%- endif %}
+{%- set field_expr = "object->" ~ packed_field.field.name %}
+{{validation_macros.validate_field(packed_field.field, field_expr, struct.name, true)}}
+{%- endif %}
+{%- endfor %}
+
+ return true;
+}
+
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 0000000000..bb5fb9c496
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -0,0 +1,161 @@
+{# TODO(yzshen): Make these templates more readable. #}
+
+{# Computes the serialized size for the specified struct.
+ |struct| is the struct definition.
+ |input_field_pattern| should be a pattern that contains one string
+ placeholder, for example, "input->%s", "p_%s". The placeholder will be
+ substituted with struct field names to refer to the input fields.
+ |context| is the name of the serialization context.
+ |input_may_be_temp| indicates whether any input may be temporary obejcts.
+ We need to assign temporary objects to local variables before passing it to
+ Serializer, because it is illegal to pass temporary objects as non-const
+ references.
+ This macro is expanded to compute seriailized size for both:
+ - user-defined structs: the input is an instance of the corresponding struct
+ wrapper class.
+ - method parameters/response parameters: the input is a list of
+ arguments.
+ It declares |size| of type size_t to store the resulting size. #}
+{%- macro get_serialized_size(struct, input_field_pattern, context,
+ input_may_be_temp=False) -%}
+ size_t size = sizeof({{struct|get_qualified_name_for_kind(internal=True)}});
+{%- for pf in struct.packed.packed_fields_in_ordinal_order
+ if pf.field.kind|is_object_kind or pf.field.kind|is_associated_kind %}
+{%- set name = pf.field.name -%}
+{%- set kind = pf.field.kind -%}
+{%- set original_input_field = input_field_pattern|format(name) %}
+{%- set input_field = "in_%s"|format(name) if input_may_be_temp
+ else original_input_field %}
+{%- if input_may_be_temp %}
+ decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};
+{%- endif %}
+
+{%- set serializer_type = kind|unmapped_type_for_serializer %}
+{%- if kind|is_union_kind %}
+ size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+ {{input_field}}, true, {{context}});
+{%- else %}
+ size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+ {{input_field}}, {{context}});
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+{# Serializes the specified struct.
+ |struct| is the struct definition.
+ |struct_display_name| is the display name for the struct that can be showed
+ in error/log messages, for example, "FooStruct", "FooMethod request".
+ |input_field_pattern| should be a pattern that contains one string
+ placeholder, for example, "input->%s", "p_%s". The placeholder will be
+ substituted with struct field names to refer to the input fields.
+ |output| is the name of the output struct instance.
+ |buffer| is the name of the Buffer instance used.
+ |context| is the name of the serialization context.
+ |input_may_be_temp|: please see the comments of get_serialized_size.
+ This macro is expanded to do serialization for both:
+ - user-defined structs: the input is an instance of the corresponding struct
+ wrapper class.
+ - method parameters/response parameters: the input is a list of
+ arguments. #}
+{%- macro serialize(struct, struct_display_name, input_field_pattern, output,
+ buffer, context, input_may_be_temp=False) -%}
+ auto {{output}} =
+ {{struct|get_qualified_name_for_kind(internal=True)}}::New({{buffer}});
+ ALLOW_UNUSED_LOCAL({{output}});
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%- set input_field = input_field_pattern|format(pf.field.name) %}
+{%- set name = pf.field.name %}
+{%- set kind = pf.field.kind %}
+{%- set serializer_type = kind|unmapped_type_for_serializer %}
+
+{%- if kind|is_object_kind or kind|is_any_handle_or_interface_kind %}
+{%- set original_input_field = input_field_pattern|format(name) %}
+{%- set input_field = "in_%s"|format(name) if input_may_be_temp
+ else original_input_field %}
+{%- if input_may_be_temp %}
+ decltype({{original_input_field}}) in_{{name}} = {{original_input_field}};
+{%- endif %}
+{%- endif %}
+
+{%- if kind|is_object_kind %}
+{%- if kind|is_array_kind or kind|is_map_kind %}
+ typename decltype({{output}}->{{name}})::BaseType* {{name}}_ptr;
+ const mojo::internal::ContainerValidateParams {{name}}_validate_params(
+ {{kind|get_container_validate_params_ctor_args|indent(10)}});
+ mojo::internal::Serialize<{{serializer_type}}>(
+ {{input_field}}, {{buffer}}, &{{name}}_ptr, &{{name}}_validate_params,
+ {{context}});
+ {{output}}->{{name}}.Set({{name}}_ptr);
+{%- elif kind|is_union_kind %}
+ auto {{name}}_ptr = &{{output}}->{{name}};
+ mojo::internal::Serialize<{{serializer_type}}>(
+ {{input_field}}, {{buffer}}, &{{name}}_ptr, true, {{context}});
+{%- else %}
+ typename decltype({{output}}->{{name}})::BaseType* {{name}}_ptr;
+ mojo::internal::Serialize<{{serializer_type}}>(
+ {{input_field}}, {{buffer}}, &{{name}}_ptr, {{context}});
+ {{output}}->{{name}}.Set({{name}}_ptr);
+{%- endif %}
+{%- if not kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ {{output}}->{{name}}.is_null(),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null {{name}} in {{struct_display_name}}");
+{%- endif %}
+
+{%- elif kind|is_any_handle_or_interface_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ {{input_field}}, &{{output}}->{{name}}, {{context}});
+{%- if not kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !mojo::internal::IsHandleOrInterfaceValid({{output}}->{{name}}),
+{%- if kind|is_associated_kind %}
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+{%- else %}
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+{%- endif %}
+ "invalid {{name}} in {{struct_display_name}}");
+{%- endif %}
+
+{%- elif kind|is_enum_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ {{input_field}}, &{{output}}->{{name}});
+
+{%- else %}
+ {{output}}->{{name}} = {{input_field}};
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+{# Deserializes the specified struct.
+ |struct| is the struct definition.
+ |input| is the name of the input struct data view. It is expected to be
+ non-null.
+ |output_field_pattern| should be a pattern that contains one string
+ placeholder, for example, "result->%s", "p_%s". The placeholder will be
+ substituted with struct field names to refer to the output fields.
+ |context| is the name of the serialization context.
+ |success| is the name of a bool variable to track success of the operation.
+ This macro is expanded to do deserialization for both:
+ - user-defined structs: the output is an instance of the corresponding
+ struct wrapper class.
+ - method parameters/response parameters: the output is a list of
+ arguments. #}
+{%- macro deserialize(struct, input, output_field_pattern, success) -%}
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%- set output_field = output_field_pattern|format(pf.field.name) %}
+{%- set name = pf.field.name %}
+{%- set kind = pf.field.kind %}
+{%- if kind|is_object_kind or kind|is_enum_kind %}
+ if (!{{input}}.Read{{name|under_to_camel}}(&{{output_field}}))
+ {{success}} = false;
+{%- elif kind|is_any_handle_kind %}
+ {{output_field}} = {{input}}.Take{{name|under_to_camel}}();
+{%- elif kind|is_any_interface_kind %}
+ {{output_field}} =
+ {{input}}.Take{{name|under_to_camel}}<decltype({{output_field}})>();
+{%- else %}
+ {{output_field}} = {{input}}.{{name}}();
+{%- 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 0000000000..835178beda
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -0,0 +1,57 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set data_view = struct|get_qualified_name_for_kind ~ "DataView" %}
+{%- set data_type = struct|get_qualified_name_for_kind(internal=True) %}
+
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct Serializer<{{data_view}}, MaybeConstUserType> {
+ using UserType = typename std::remove_const<MaybeConstUserType>::type;
+ using Traits = StructTraits<{{data_view}}, UserType>;
+
+ static size_t PrepareToSerialize(MaybeConstUserType& input,
+ SerializationContext* context) {
+ if (CallIsNullIfExists<Traits>(input))
+ return 0;
+
+ void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
+ ALLOW_UNUSED_LOCAL(custom_context);
+
+ {{struct_macros.get_serialized_size(
+ struct, "CallWithContext(Traits::%s, input, custom_context)",
+ "context", True)|indent(2)}}
+ return size;
+ }
+
+ static void Serialize(MaybeConstUserType& input,
+ Buffer* buffer,
+ {{data_type}}** output,
+ SerializationContext* context) {
+ if (CallIsNullIfExists<Traits>(input)) {
+ *output = nullptr;
+ return;
+ }
+
+ void* custom_context = CustomContextHelper<Traits>::GetNext(context);
+
+ {{struct_macros.serialize(
+ struct, struct.name ~ " struct",
+ "CallWithContext(Traits::%s, input, custom_context)", "result",
+ "buffer", "context", True)|indent(2)}}
+ *output = result;
+
+ CustomContextHelper<Traits>::TearDown(input, custom_context);
+ }
+
+ static bool Deserialize({{data_type}}* input,
+ UserType* output,
+ SerializationContext* context) {
+ if (!input)
+ return CallSetToNullIfExists<Traits>(output);
+
+ {{data_view}} data_view(input, context);
+ return Traits::Read(data_view, output);
+ }
+};
+
+} // namespace internal
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl
new file mode 100644
index 0000000000..1b7cf8954b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl
@@ -0,0 +1,32 @@
+{%- set mojom_type = struct|get_qualified_name_for_kind %}
+
+template <>
+struct {{export_attribute}} StructTraits<{{mojom_type}}::DataView,
+ {{mojom_type}}Ptr> {
+ static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
+ static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
+
+{%- for field in struct.fields %}
+{%- set return_ref = field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+{# We want the field accessor to be const whenever possible to allow
+ structs to be used as map keys.
+ TODO(tibell): Make this check more precise to deal with e.g.
+ custom types which don't contain handles but require non-const
+ reference for serialization. #}
+{%- set maybe_const = "" if field.kind|contains_handles_or_interfaces else "const" %}
+{%- if return_ref %}
+ static {{maybe_const}} decltype({{mojom_type}}::{{field.name}})& {{field.name}}(
+ {{maybe_const}} {{mojom_type}}Ptr& input) {
+ return input->{{field.name}};
+ }
+{%- else %}
+ static decltype({{mojom_type}}::{{field.name}}) {{field.name}}(
+ const {{mojom_type}}Ptr& input) {
+ return input->{{field.name}};
+ }
+{%- endif %}
+{%- endfor %}
+
+ static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output);
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl
new file mode 100644
index 0000000000..f84337f5bf
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl
@@ -0,0 +1,14 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set mojom_type = struct|get_qualified_name_for_kind %}
+
+// static
+bool StructTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read(
+ {{mojom_type}}::DataView input,
+ {{mojom_type}}Ptr* output) {
+ bool success = true;
+ {{mojom_type}}Ptr result({{mojom_type}}::New());
+ {{struct_macros.deserialize(struct, "input", "result->%s",
+ "success")|indent(4)}}
+ *output = std::move(result);
+ return success;
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
new file mode 100644
index 0000000000..5973ba294b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
@@ -0,0 +1,92 @@
+class {{union.name}}DataView {
+ public:
+ using Tag = internal::{{union.name}}_Data::{{union.name}}_Tag;
+
+ {{union.name}}DataView() {}
+
+ {{union.name}}DataView(
+ internal::{{union.name}}_Data* data,
+ mojo::internal::SerializationContext* context)
+{%- if union|requires_context_for_data_view %}
+ : data_(data), context_(context) {}
+{%- else %}
+ : data_(data) {}
+{%- endif %}
+
+ bool is_null() const {
+ // For inlined unions, |data_| is always non-null. In that case we need to
+ // check |data_->is_null()|.
+ return !data_ || data_->is_null();
+ }
+
+ Tag tag() const { return data_->tag; }
+
+{%- for field in union.fields %}
+{%- set kind = field.kind %}
+{%- set name = field.name %}
+ bool is_{{name}}() const { return data_->tag == Tag::{{name|upper}}; }
+
+{%- if kind|is_object_kind %}
+ inline void Get{{name|under_to_camel}}DataView(
+ {{kind|cpp_data_view_type}}* output);
+
+ template <typename UserType>
+ WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
+ DCHECK(is_{{name}}());
+ return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ data_->data.f_{{name}}.Get(), output, context_);
+ }
+
+{%- elif kind|is_enum_kind %}
+ template <typename UserType>
+ WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const {
+ DCHECK(is_{{name}}());
+ return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ data_->data.f_{{name}}, output);
+ }
+
+ {{kind|cpp_data_view_type}} {{name}}() const {
+ DCHECK(is_{{name}}());
+ return static_cast<{{kind|cpp_data_view_type}}>(
+ data_->data.f_{{name}});
+ }
+
+{%- elif kind|is_any_handle_kind %}
+ {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() {
+ DCHECK(is_{{name}}());
+ {{kind|cpp_data_view_type}} result;
+ bool ret =
+ mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ &data_->data.f_{{name}}, &result, context_);
+ DCHECK(ret);
+ return result;
+ }
+
+{%- elif kind|is_any_interface_kind %}
+ template <typename UserType>
+ UserType Take{{name|under_to_camel}}() {
+ DCHECK(is_{{name}}());
+ UserType result;
+ bool ret =
+ mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ &data_->data.f_{{name}}, &result, context_);
+ DCHECK(ret);
+ return result;
+ }
+
+{%- else %}
+ {{kind|cpp_data_view_type}} {{name}}() const {
+ DCHECK(is_{{name}}());
+ return data_->data.f_{{name}};
+ }
+
+{%- endif %}
+{%- endfor %}
+
+ private:
+ internal::{{union.name}}_Data* data_ = nullptr;
+{%- if union|requires_context_for_data_view %}
+ mojo::internal::SerializationContext* context_ = nullptr;
+{%- endif %}
+};
+
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl
new file mode 100644
index 0000000000..6da9280a73
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl
@@ -0,0 +1,12 @@
+{%- for field in union.fields %}
+{%- set kind = field.kind %}
+{%- set name = field.name %}
+
+{%- if kind|is_object_kind %}
+inline void {{union.name}}DataView::Get{{name|under_to_camel}}DataView(
+ {{kind|cpp_data_view_type}}* output) {
+ DCHECK(is_{{name}}());
+ *output = {{kind|cpp_data_view_type}}(data_->data.f_{{name}}.Get(), context_);
+}
+{%- endif %}
+{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
new file mode 100644
index 0000000000..005ba76b61
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
@@ -0,0 +1,56 @@
+{%- set class_name = union.name ~ "_Data" -%}
+{%- set enum_name = union.name ~ "_Tag" -%}
+{%- import "struct_macros.tmpl" as struct_macros %}
+
+class {{class_name}} {
+ public:
+ // Used to identify Mojom Union Data Classes.
+ typedef void MojomUnionDataType;
+
+ {{class_name}}() {}
+ // Do nothing in the destructor since it won't be called when it is a
+ // non-inlined union.
+ ~{{class_name}}() {}
+
+ 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::ValidationContext* validation_context,
+ bool inlined);
+
+ bool is_null() const { return size == 0; }
+
+ void set_null() {
+ size = 0U;
+ tag = static_cast<{{enum_name}}>(0);
+ data.unknown = 0U;
+ }
+
+ enum class {{enum_name}} : uint32_t {
+{% for field in union.fields %}
+ {{field.name|upper}},
+{%- endfor %}
+ };
+
+ // A note on layout:
+ // "Each non-static data member is allocated as if it were the sole member of
+ // a struct." - Section 9.5.2 ISO/IEC 14882:2011 (The C++ Spec)
+ union MOJO_ALIGNAS(8) Union_ {
+{%- for field in union.fields %}
+{%- if field.kind.spec == 'b' %}
+ uint8_t f_{{field.name}} : 1;
+{%- else %}
+ {{field.kind|cpp_union_field_type}} f_{{field.name}};
+{%- endif %}
+{%- endfor %}
+ uint64_t unknown;
+ };
+
+ uint32_t size;
+ {{enum_name}} tag;
+ Union_ data;
+};
+static_assert(sizeof({{class_name}}) == mojo::internal::kUnionDataSize,
+ "Bad sizeof({{class_name}})");
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
new file mode 100644
index 0000000000..af5ea9f8a8
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
@@ -0,0 +1,47 @@
+{%- import "validation_macros.tmpl" as validation_macros %}
+{%- set class_name = union.name ~ "_Data" %}
+{%- set enum_name = union.name ~ "_Tag" -%}
+
+// static
+bool {{class_name}}::Validate(
+ const void* data,
+ mojo::internal::ValidationContext* validation_context,
+ bool inlined) {
+ if (!data) {
+ DCHECK(!inlined);
+ return true;
+ }
+
+ // If it is inlined, the alignment is already enforced by its enclosing
+ // object. We don't have to validate that.
+ DCHECK(!inlined || mojo::internal::IsAligned(data));
+
+ if (!inlined &&
+ !mojo::internal::ValidateNonInlinedUnionHeaderAndClaimMemory(
+ data, validation_context)) {
+ return false;
+ }
+
+ const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
+ ALLOW_UNUSED_LOCAL(object);
+
+ if (inlined && object->is_null())
+ return true;
+
+ switch (object->tag) {
+{% for field in union.fields %}
+ case {{enum_name}}::{{field.name|upper}}: {
+{%- set field_expr = "object->data.f_" ~ field.name %}
+{{validation_macros.validate_field(field, field_expr, union.name, false)|indent(4)}}
+ return true;
+ }
+{%- endfor %}
+ default: {
+ ReportValidationError(
+ validation_context,
+ mojo::internal::VALIDATION_ERROR_UNKNOWN_UNION_TAG,
+ "unknown tag in {{union.name}}");
+ return false;
+ }
+ }
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
new file mode 100644
index 0000000000..b589ae9147
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
@@ -0,0 +1,141 @@
+{%- set data_view = union|get_qualified_name_for_kind ~ "DataView" %}
+{%- set data_type = union|get_qualified_name_for_kind(internal=True) %}
+
+namespace internal {
+
+template <typename MaybeConstUserType>
+struct Serializer<{{data_view}}, MaybeConstUserType> {
+ using UserType = typename std::remove_const<MaybeConstUserType>::type;
+ using Traits = UnionTraits<{{data_view}}, UserType>;
+
+ static size_t PrepareToSerialize(MaybeConstUserType& input,
+ bool inlined,
+ SerializationContext* context) {
+ size_t size = inlined ? 0 : sizeof({{data_type}});
+
+ if (CallIsNullIfExists<Traits>(input))
+ return size;
+
+ void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
+ ALLOW_UNUSED_LOCAL(custom_context);
+
+ switch (CallWithContext(Traits::GetTag, input, custom_context)) {
+{%- for field in union.fields %}
+{%- set name = field.name %}
+ case {{data_view}}::Tag::{{name|upper}}: {
+{%- if field.kind|is_object_kind or field.kind|is_associated_kind %}
+{%- set kind = field.kind %}
+{%- set serializer_type = kind|unmapped_type_for_serializer %}
+ decltype(CallWithContext(Traits::{{name}}, input, custom_context))
+ in_{{name}} = CallWithContext(Traits::{{name}}, input,
+ custom_context);
+{%- if kind|is_union_kind %}
+ size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+ in_{{name}}, false, context);
+{%- else %}
+ size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+ in_{{name}}, context);
+{%- endif %}
+{%- endif %}
+ break;
+ }
+{%- endfor %}
+ }
+ return size;
+ }
+
+ static void Serialize(MaybeConstUserType& input,
+ Buffer* buffer,
+ {{data_type}}** output,
+ bool inlined,
+ SerializationContext* context) {
+ if (CallIsNullIfExists<Traits>(input)) {
+ if (inlined)
+ (*output)->set_null();
+ else
+ *output = nullptr;
+ return;
+ }
+
+ void* custom_context = CustomContextHelper<Traits>::GetNext(context);
+
+ if (!inlined)
+ *output = {{data_type}}::New(buffer);
+
+ {{data_type}}* result = *output;
+ ALLOW_UNUSED_LOCAL(result);
+ // TODO(azani): Handle unknown and objects.
+ // Set the not-null flag.
+ result->size = kUnionDataSize;
+ result->tag = CallWithContext(Traits::GetTag, input, custom_context);
+ switch (result->tag) {
+{%- for field in union.fields %}
+{%- set name = field.name %}
+{%- set kind = field.kind %}
+{%- set serializer_type = kind|unmapped_type_for_serializer %}
+ case {{data_view}}::Tag::{{field.name|upper}}: {
+ decltype(CallWithContext(Traits::{{name}}, input, custom_context))
+ in_{{name}} = CallWithContext(Traits::{{name}}, input,
+ custom_context);
+{%- if kind|is_object_kind %}
+ typename decltype(result->data.f_{{name}})::BaseType* ptr;
+{%- if kind|is_union_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ in_{{name}}, buffer, &ptr, false, context);
+{%- elif kind|is_array_kind or kind|is_map_kind %}
+ const ContainerValidateParams {{name}}_validate_params(
+ {{kind|get_container_validate_params_ctor_args|indent(16)}});
+ mojo::internal::Serialize<{{serializer_type}}>(
+ in_{{name}}, buffer, &ptr, &{{name}}_validate_params, context);
+{%- else %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ in_{{name}}, buffer, &ptr, context);
+{%- endif %}
+ result->data.f_{{name}}.Set(ptr);
+{%- if not kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !ptr, mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null {{name}} in {{union.name}} union");
+{%- endif %}
+
+{%- elif kind|is_any_handle_or_interface_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ in_{{name}}, &result->data.f_{{name}}, context);
+{%- if not kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !mojo::internal::IsHandleOrInterfaceValid(result->data.f_{{name}}),
+{%- if kind|is_associated_kind %}
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+{%- else %}
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+{%- endif %}
+ "invalid {{name}} in {{union.name}} union");
+{%- endif %}
+
+{%- elif kind|is_enum_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ in_{{name}}, &result->data.f_{{name}});
+
+{%- else %}
+ result->data.f_{{name}} = in_{{name}};
+{%- endif %}
+ break;
+ }
+{%- endfor %}
+ }
+
+ CustomContextHelper<Traits>::TearDown(input, custom_context);
+ }
+
+ static bool Deserialize({{data_type}}* input,
+ UserType* output,
+ SerializationContext* context) {
+ if (!input || input->is_null())
+ return CallSetToNullIfExists<Traits>(output);
+
+ {{data_view}} data_view(input, context);
+ return Traits::Read(data_view, output);
+ }
+};
+
+} // namespace internal
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl
new file mode 100644
index 0000000000..4933e57871
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl
@@ -0,0 +1,24 @@
+{%- set mojom_type = union|get_qualified_name_for_kind %}
+
+template <>
+struct {{export_attribute}} UnionTraits<{{mojom_type}}::DataView,
+ {{mojom_type}}Ptr> {
+ static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
+ static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
+
+ static {{mojom_type}}::Tag GetTag(const {{mojom_type}}Ptr& input) {
+ return input->which();
+ }
+
+{%- for field in union.fields %}
+{%- set maybe_const_in = "" if field.kind|contains_handles_or_interfaces else "const" %}
+{%- set maybe_const_out = "" if field.kind|contains_handles_or_interfaces or not field.kind|is_reference_kind else "const" %}
+{# We want the field accessor to be const whenever possible to allow
+ structs to be used as map keys. #}
+ static {{maybe_const_out}} {{field.kind|cpp_union_trait_getter_return_type}} {{field.name}}({{maybe_const_in}} {{mojom_type}}Ptr& input) {
+ return input->get_{{field.name}}();
+ }
+{%- endfor %}
+
+ static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output);
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl
new file mode 100644
index 0000000000..cde3f95669
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl
@@ -0,0 +1,47 @@
+{%- set mojom_type = union|get_qualified_name_for_kind %}
+
+// static
+bool UnionTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read(
+ {{mojom_type}}::DataView input,
+ {{mojom_type}}Ptr* output) {
+ *output = {{mojom_type}}::New();
+ {{mojom_type}}Ptr& result = *output;
+
+ internal::UnionAccessor<{{mojom_type}}> result_acc(result.get());
+ switch (input.tag()) {
+{%- for field in union.fields %}
+ case {{mojom_type}}::Tag::{{field.name|upper}}: {
+{%- set name = field.name %}
+{%- set kind = field.kind %}
+{%- set serializer_type = kind|unmapped_type_for_serializer %}
+{%- if kind|is_object_kind %}
+ result_acc.SwitchActive({{mojom_type}}::Tag::{{name|upper}});
+ if (!input.Read{{name|under_to_camel}}(result_acc.data()->{{name}}))
+ return false;
+
+{%- elif kind|is_any_handle_kind %}
+ auto result_{{name}} = input.Take{{name|under_to_camel}}();
+ result->set_{{name}}(std::move(result_{{name}}));
+
+{%- elif kind|is_any_interface_kind %}
+ auto result_{{name}} =
+ input.Take{{name|under_to_camel}}<typename std::remove_reference<decltype(result->get_{{name}}())>::type>();
+ result->set_{{name}}(std::move(result_{{name}}));
+
+{%- elif kind|is_enum_kind %}
+ decltype(result->get_{{name}}()) result_{{name}};
+ if (!input.Read{{name|under_to_camel}}(&result_{{name}}))
+ return false;
+ result->set_{{name}}(result_{{name}});
+
+{%- else %}
+ result->set_{{name}}(input.{{name}}());
+{%- endif %}
+ break;
+ }
+{%- endfor %}
+ default:
+ return false;
+ }
+ return true;
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
new file mode 100644
index 0000000000..a50a585c09
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
@@ -0,0 +1,82 @@
+{#- Validates the specified field, which is supposed to be an object
+ (struct/array/string/map/union). If it is a union, |union_is_inlined|
+ indicates whether the union is inlined. (Nested unions are not inlined.)
+ This macro is expanded by the Validate() method. #}
+{%- macro validate_object(field, field_expr, object_name, union_is_inlined) %}
+{%- set name = field.name %}
+{%- set kind = field.kind %}
+{%- if not kind|is_nullable_kind %}
+{%- if kind|is_union_kind and union_is_inlined %}
+ if (!mojo::internal::ValidateInlinedUnionNonNullable(
+ {{field_expr}}, "null {{name}} field in {{object_name}}",
+ validation_context)) {
+ return false;
+ }
+{%- else %}
+ if (!mojo::internal::ValidatePointerNonNullable(
+ {{field_expr}}, "null {{name}} field in {{object_name}}",
+ validation_context)) {
+ return false;
+ }
+{%- endif %}
+{%- endif %}
+{%- if kind|is_array_kind or kind|is_string_kind or kind|is_map_kind %}
+ const mojo::internal::ContainerValidateParams {{name}}_validate_params(
+ {{kind|get_container_validate_params_ctor_args|indent(6)}});
+ if (!mojo::internal::ValidateContainer({{field_expr}}, validation_context,
+ &{{name}}_validate_params)) {
+ return false;
+ }
+{%- elif kind|is_struct_kind %}
+ if (!mojo::internal::ValidateStruct({{field_expr}}, validation_context))
+ return false;
+{%- elif kind|is_union_kind %}
+{%- if union_is_inlined %}
+ if (!mojo::internal::ValidateInlinedUnion({{field_expr}}, validation_context))
+ return false;
+{%- else %}
+ if (!mojo::internal::ValidateNonInlinedUnion({{field_expr}},
+ validation_context))
+ return false;
+{%- endif %}
+{%- else %}
+#error Not reached!
+{%- endif %}
+{%- endmacro %}
+
+{#- Validates the specified field, which is supposed to be a handle,
+ an interface, an associated interface or an associated interface request.
+ This macro is expanded by the Validate() method. #}
+{%- macro validate_handle_or_interface(field, field_expr, object_name) %}
+{%- set name = field.name %}
+{%- set kind = field.kind %}
+{%- if not kind|is_nullable_kind %}
+ if (!mojo::internal::ValidateHandleOrInterfaceNonNullable(
+ {{field_expr}},
+ "invalid {{name}} field in {{object_name}}", validation_context)) {
+ return false;
+ }
+{%- endif %}
+ if (!mojo::internal::ValidateHandleOrInterface({{field_expr}},
+ validation_context)) {
+ return false;
+ }
+{%- endmacro %}
+
+{#- Validates the specified field, which is supposed to be an enum.
+ This macro is expanded by the Validate() method. #}
+{%- macro validate_enum(field, field_expr) %}
+ if (!{{field.kind|get_qualified_name_for_kind(internal=True,flatten_nested_kind=True)}}
+ ::Validate({{field_expr}}, validation_context))
+ return false;
+{%- endmacro %}
+
+{%- macro validate_field(field, field_expr, object_name, union_is_inlined) %}
+{%- if field.kind|is_object_kind -%}
+{{validate_object(field, field_expr, object_name, union_is_inlined)}}
+{%- elif field.kind|is_any_handle_or_interface_kind -%}
+{{validate_handle_or_interface(field, field_expr, object_name)}}
+{%- elif field.kind|is_enum_kind %}
+{{validate_enum(field, field_expr)}}
+{%- endif %}
+{%- endmacro %}
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 0000000000..7ad9b4e1bc
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -0,0 +1,94 @@
+class {{export_attribute}} {{struct.name}} {
+ public:
+ using DataView = {{struct.name}}DataView;
+ using Data_ = internal::{{struct.name}}_Data;
+
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+ using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}};
+{%- endfor %}
+
+{#--- Constants #}
+{%- for constant in struct.constants %}
+ static {{constant|format_constant_declaration(nested=True)}};
+{%- endfor %}
+
+ template <typename... Args>
+ static {{struct.name}}Ptr New(Args&&... args) {
+ return {{struct.name}}Ptr(
+ base::in_place,
+ std::forward<Args>(args)...);
+ }
+
+ template <typename U>
+ static {{struct.name}}Ptr From(const U& u) {
+ return mojo::TypeConverter<{{struct.name}}Ptr, U>::Convert(u);
+ }
+
+ template <typename U>
+ U To() const {
+ return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this);
+ }
+
+{% for constructor in struct|struct_constructors %}
+ {% if constructor.params|length == 1 %}explicit {% endif %}{{struct.name}}(
+{%- for field in constructor.params %}
+{%- set type = field.kind|cpp_wrapper_param_type %}
+{%- set name = field.name %}
+ {{type}} {{name}}
+{%- if not loop.last -%},{%- endif %}
+{%- endfor %});
+{% endfor %}
+ ~{{struct.name}}();
+
+ // Clone() is a template so it is only instantiated if it is used. Thus, the
+ // bindings generator does not need to know whether Clone() or copy
+ // constructor/assignment are available for members.
+ template <typename StructPtrType = {{struct.name}}Ptr>
+ {{struct.name}}Ptr Clone() const;
+
+ // Equals() is a template so it is only instantiated if it is used. Thus, the
+ // bindings generator does not need to know whether Equals() or == operator
+ // are available for members.
+ template <typename T,
+ typename std::enable_if<std::is_same<
+ T, {{struct.name}}>::value>::type* = nullptr>
+ bool Equals(const T& other) const;
+
+{%- if struct|is_hashable %}
+ size_t Hash(size_t seed) const;
+{%- endif %}
+
+{%- set serialization_result_type = "WTF::Vector<uint8_t>"
+ if for_blink else "std::vector<uint8_t>" %}
+
+ template <typename UserType>
+ static {{serialization_result_type}} Serialize(UserType* input) {
+ return mojo::internal::StructSerializeImpl<
+ {{struct.name}}::DataView, {{serialization_result_type}}>(input);
+ }
+
+ template <typename UserType>
+ static bool Deserialize(const {{serialization_result_type}}& input,
+ UserType* output) {
+ return mojo::internal::StructDeserializeImpl<
+ {{struct.name}}::DataView, {{serialization_result_type}}>(
+ input, output, Validate);
+ }
+
+{#--- Struct members #}
+{% for field in struct.fields %}
+{%- set type = field.kind|cpp_wrapper_type %}
+{%- set name = field.name %}
+ {{type}} {{name}};
+{%- endfor %}
+
+ private:
+ static bool Validate(const void* data,
+ mojo::internal::ValidationContext* validation_context);
+
+{%- if struct|contains_move_only_members %}
+ DISALLOW_COPY_AND_ASSIGN({{struct.name}});
+{%- endif %}
+};
+
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 0000000000..ab8c22d49c
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -0,0 +1,39 @@
+{% for constructor in struct|struct_constructors %}
+{{struct.name}}::{{struct.name}}(
+{%- for field in constructor.params %}
+{%- set type = field.kind|cpp_wrapper_param_type %}
+{%- set name = field.name %}
+ {{type}} {{name}}_in
+{%- if not loop.last -%},{%- endif %}
+{%- endfor %})
+{%- for field, is_parameter in constructor.fields %}
+{%- set name = field.name %}
+ {% if loop.first %}:{% else %} {% endif %} {{name}}(
+{%- if is_parameter -%}
+std::move({{name}}_in)
+{%- else -%}
+{{ field|default_value }}
+{%- endif -%}
+){% if not loop.last %},{% endif %}
+{%- endfor %} {}
+{% endfor %}
+{{struct.name}}::~{{struct.name}}() = default;
+
+{%- if struct|is_hashable %}
+size_t {{struct.name}}::Hash(size_t seed) const {
+{%- for field in struct.fields %}
+{%- if for_blink %}
+ seed = mojo::internal::WTFHash(seed, this->{{field.name}});
+{%- else %}
+ seed = mojo::internal::Hash(seed, this->{{field.name}});
+{%- endif %}
+{%- endfor %}
+ return seed;
+}
+{%- endif %}
+
+bool {{struct.name}}::Validate(
+ const void* data,
+ mojo::internal::ValidationContext* validation_context) {
+ return Data_::Validate(data, validation_context);
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
new file mode 100644
index 0000000000..feb861569f
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
@@ -0,0 +1,20 @@
+template <typename StructPtrType>
+{{struct.name}}Ptr {{struct.name}}::Clone() const {
+ return New(
+{%- for field in struct.fields %}
+ mojo::Clone({{field.name}})
+{%- if not loop.last -%},{%- endif %}
+{%- endfor %}
+ );
+}
+
+template <typename T,
+ typename std::enable_if<std::is_same<
+ T, {{struct.name}}>::value>::type*>
+bool {{struct.name}}::Equals(const T& other) const {
+{%- for field in struct.fields %}
+ if (!mojo::internal::Equals(this->{{field.name}}, other.{{field.name}}))
+ return false;
+{%- endfor %}
+ return true;
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
new file mode 100644
index 0000000000..8b7cf9e6b1
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
@@ -0,0 +1,80 @@
+class {{export_attribute}} {{union.name}} {
+ public:
+ using DataView = {{union.name}}DataView;
+ using Data_ = internal::{{union.name}}_Data;
+ using Tag = Data_::{{union.name}}_Tag;
+
+ static {{union.name}}Ptr New();
+
+ template <typename U>
+ static {{union.name}}Ptr From(const U& u) {
+ return mojo::TypeConverter<{{union.name}}Ptr, U>::Convert(u);
+ }
+
+ template <typename U>
+ U To() const {
+ return mojo::TypeConverter<U, {{union.name}}>::Convert(*this);
+ }
+
+ {{union.name}}();
+ ~{{union.name}}();
+
+ // Clone() is a template so it is only instantiated if it is used. Thus, the
+ // bindings generator does not need to know whether Clone() or copy
+ // constructor/assignment are available for members.
+ template <typename UnionPtrType = {{union.name}}Ptr>
+ {{union.name}}Ptr Clone() const;
+
+ // Equals() is a template so it is only instantiated if it is used. Thus, the
+ // bindings generator does not need to know whether Equals() or == operator
+ // are available for members.
+ template <typename T,
+ typename std::enable_if<std::is_same<
+ T, {{union.name}}>::value>::type* = nullptr>
+ bool Equals(const T& other) const;
+
+{%- if union|is_hashable %}
+ size_t Hash(size_t seed) const;
+{%- endif %}
+
+ Tag which() const {
+ return tag_;
+ }
+
+{% for field in union.fields %}
+ bool is_{{field.name}}() const { return tag_ == Tag::{{field.name|upper}}; }
+
+ {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const {
+ DCHECK(tag_ == Tag::{{field.name|upper}});
+{%- if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ return *(data_.{{field.name}});
+{%- else %}
+ return data_.{{field.name}};
+{%- endif %}
+ }
+
+ void set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}});
+{%- endfor %}
+
+ private:
+ friend class mojo::internal::UnionAccessor<{{union.name}}>;
+ union Union_ {
+ Union_() {}
+ ~Union_() {}
+
+{%- for field in union.fields %}
+{%- if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ {{field.kind|cpp_wrapper_type}}* {{field.name}};
+{%- else %}
+ {{field.kind|cpp_wrapper_type}} {{field.name}};
+{%- endif %}
+{%- endfor %}
+ };
+ void SwitchActive(Tag new_active);
+ void SetActive(Tag new_active);
+ void DestroyActive();
+ Tag tag_;
+ Union_ data_;
+};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
new file mode 100644
index 0000000000..b9e416a9f4
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
@@ -0,0 +1,85 @@
+// static
+{{union.name}}Ptr {{union.name}}::New() {
+ return {{union.name}}Ptr(base::in_place);
+}
+
+{{union.name}}::{{union.name}}() {
+ // TODO(azani): Implement default values here when/if we support them.
+ // TODO(azani): Set to UNKNOWN when unknown is implemented.
+ SetActive(static_cast<Tag>(0));
+}
+
+{{union.name}}::~{{union.name}}() {
+ DestroyActive();
+}
+
+{% for field in union.fields %}
+void {{union.name}}::set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}}) {
+ SwitchActive(Tag::{{field.name|upper}});
+{% if field.kind|is_string_kind %}
+ *(data_.{{field.name}}) = {{field.name}};
+{% elif field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ *(data_.{{field.name}}) = std::move({{field.name}});
+{%- else %}
+ data_.{{field.name}} = {{field.name}};
+{%- endif %}
+}
+{%- endfor %}
+
+void {{union.name}}::SwitchActive(Tag new_active) {
+ if (new_active == tag_) {
+ return;
+ }
+
+ DestroyActive();
+ SetActive(new_active);
+}
+
+void {{union.name}}::SetActive(Tag new_active) {
+ switch (new_active) {
+{% for field in union.fields %}
+ case Tag::{{field.name|upper}}:
+{% if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ data_.{{field.name}} = new {{field.kind|cpp_wrapper_type}}();
+{%- endif %}
+ break;
+{%- endfor %}
+ }
+
+ tag_ = new_active;
+}
+
+void {{union.name}}::DestroyActive() {
+ switch (tag_) {
+{% for field in union.fields %}
+ case Tag::{{field.name|upper}}:
+{% if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ delete data_.{{field.name}};
+{%- endif %}
+ break;
+{%- endfor %}
+ }
+}
+
+{%- if union|is_hashable %}
+size_t {{union.name}}::Hash(size_t seed) const {
+ seed = mojo::internal::HashCombine(seed, static_cast<uint32_t>(tag_));
+ switch (tag_) {
+{% for field in union.fields %}
+ case Tag::{{field.name|upper}}:
+{%- if for_blink %}
+ return mojo::internal::WTFHash(seed, data_.{{field.name}});
+{%- else %}
+ return mojo::internal::Hash(seed, data_.{{field.name}});
+{%- endif %}
+{%- endfor %}
+ default:
+ NOTREACHED();
+ return seed;
+ }
+}
+
+{%- endif %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
new file mode 100644
index 0000000000..4c4851fa83
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
@@ -0,0 +1,41 @@
+template <typename UnionPtrType>
+{{union.name}}Ptr {{union.name}}::Clone() const {
+ // Use UnionPtrType to prevent the compiler from trying to compile this
+ // without being asked.
+ UnionPtrType rv(New());
+ switch (tag_) {
+{%- for field in union.fields %}
+ case Tag::{{field.name|upper}}:
+{%- if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ rv->set_{{field.name}}(mojo::Clone(*data_.{{field.name}}));
+{%- else %}
+ rv->set_{{field.name}}(mojo::Clone(data_.{{field.name}}));
+{%- endif %}
+ break;
+{%- endfor %}
+ };
+ return rv;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_same<
+ T, {{union.name}}>::value>::type*>
+bool {{union.name}}::Equals(const T& other) const {
+ if (tag_ != other.which())
+ return false;
+
+ switch (tag_) {
+{%- for field in union.fields %}
+ case Tag::{{field.name|upper}}:
+{%- if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ return mojo::internal::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}}));
+{%- else %}
+ return mojo::internal::Equals(data_.{{field.name}}, other.data_.{{field.name}});
+{%- endif %}
+{%- endfor %}
+ };
+
+ return false;
+}
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 0000000000..db193e29a3
--- /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 0000000000..0a4e29956b
--- /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/data_types_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
new file mode 100644
index 0000000000..4c0823cce6
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
@@ -0,0 +1,418 @@
+{%- from "constant_definition.tmpl" import constant_def %}
+{%- from "enum_definition.tmpl" import enum_def %}
+
+{%- macro equality(kind, v1, v2, ne=False) -%}
+{%- if kind|is_reference_kind -%}
+{%- if kind|is_array_kind -%}
+{%- if kind.kind|is_reference_kind -%}
+{%- if ne %}!{%- endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}})
+{%- else -%}
+{%- if ne %}!{%- endif %}java.util.Arrays.equals({{v1}}, {{v2}})
+{%- endif -%}
+{%- else -%}
+{%- if ne %}!{%- endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}})
+{%- endif -%}
+{%- else -%}
+{{v1}} {%- if ne %}!={%- else %}=={%- endif %} {{v2}}
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro hash(kind, v) -%}
+{%- if kind|is_array_kind -%}
+{%- if kind.kind|is_reference_kind -%}
+java.util.Arrays.deepHashCode({{v}})
+{%- else -%}
+java.util.Arrays.hashCode({{v}})
+{%- endif -%}
+{%- else -%}
+org.chromium.mojo.bindings.BindingsHelper.hashCode({{v}})
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro array_element_size(kind) -%}
+{%- if kind|is_union_kind %}
+org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE
+{%- else -%}
+org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro encode(variable, kind, offset, bit, level=0, check_for_null=True) %}
+{%- if kind|is_pointer_array_kind or kind|is_union_array_kind %}
+{%- set sub_kind = kind.kind %}
+{%- if check_for_null %}
+if ({{variable}} == null) {
+ encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
+} else {
+{%- else %}
+{
+{%- endif %}
+{%- if kind|is_pointer_array_kind %}
+{%- set encodePointer = 'encodePointerArray' %}
+{%- else %}
+{%- set encodePointer = 'encodeUnionArray' %}
+{%- endif %}
+ org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.{{encodePointer}}({{variable}}.length, {{offset}}, {{kind|array_expected_length}});
+ for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
+ {{encode(variable~'[i'~level~']', sub_kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(sub_kind) ~ ' * i'~level, 0, level+1)|indent(8)}}
+ }
+}
+{%- elif kind|is_map_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}}.encoderForMap({{offset}});
+ int size{{level}} = {{variable}}.size();
+ {{kind.key_kind|java_type}}[] keys{{level}} = {{kind.key_kind|array|new_array('size'~level)}};
+ {{kind.value_kind|java_type}}[] values{{level}} = {{kind.value_kind|array|new_array('size'~level)}};
+ int index{{level}} = 0;
+ for (java.util.Map.Entry<{{kind.key_kind|java_type(true)}}, {{kind.value_kind|java_type(true)}}> entry{{level}} : {{variable}}.entrySet()) {
+ keys{{level}}[index{{level}}] = entry{{level}}.getKey();
+ values{{level}}[index{{level}}] = entry{{level}}.getValue();
+ ++index{{level}};
+ }
+ {{encode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1, False)|indent(4)}}
+ {{encode('values'~level, kind.value_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1, False)|indent(4)}}
+}
+{%- 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 or
+ kind|is_union_array_kind or
+ kind|is_map_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 or is_map_kind #}
+{%- if kind|is_nullable_kind %}
+if (decoder{{level+1}} == null) {
+ {{variable}} = null;
+} else {
+{%- else %}
+{
+{%- endif %}
+{%- if kind|is_map_kind %}
+ decoder{{level+1}}.readDataHeaderForMap();
+ {{kind.key_kind|java_type}}[] keys{{level}};
+ {{kind.value_kind|java_type}}[] values{{level}};
+ {
+ {{decode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1)|indent(8)}}
+ }
+ {
+ {{decode('values'~level, kind.value_kind|array('keys'~level~'.length'), 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1)|indent(8)}}
+ }
+ {{variable}} = new java.util.HashMap<{{kind.key_kind|java_type(true)}}, {{kind.value_kind|java_type(true)}}>();
+ for (int index{{level}} = 0; index{{level}} < keys{{level}}.length; ++index{{level}}) {
+ {{variable}}.put(keys{{level}}[index{{level}}], values{{level}}[index{{level}}]);
+ }
+{%- else %}
+ org.chromium.mojo.bindings.DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{kind|array_expected_length}});
+ {{variable}} = {{kind|new_array('si'~(level+1)~'.elementsOrVersion')}};
+ for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.elementsOrVersion; ++i{{level+1}}) {
+ {{decode(variable~'[i'~(level+1)~']', kind.kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(kind.kind) ~' * i'~(level+1), 0, level+1)|indent(8)}}
+ }
+{%- endif %}
+}
+{%- endif %}
+{%- elif kind|is_union_kind %}
+{{variable}} = {{kind|java_type}}.decode(decoder{{level}}, {{offset}});
+{%- else %}
+{{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}};
+{%- if kind|is_array_kind and kind.kind|is_enum_kind %}
+{%- if kind|is_nullable_kind %}
+if ({{variable}} != null) {
+{%- else %}
+{
+{%- endif %}
+ for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
+ {{kind.kind|java_class_for_enum}}.validate({{variable}}[i{{level}}]);
+ }
+}
+{%- elif kind|is_enum_kind %}
+ {{kind|java_class_for_enum}}.validate({{variable}});
+{%- endif %}
+{%- 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.versions[-1].num_bytes}};
+ private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {
+{%- for version in struct.versions -%}
+ new org.chromium.mojo.bindings.DataHeader({{version.num_bytes}}, {{version.version}}){%- if not loop.last %}, {%- endif -%}
+{%- endfor -%}
+ };
+ private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[{{struct.versions|length - 1}}];
+{%- 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 %}
+
+ private {{struct|name}}(int version) {
+ super(STRUCT_SIZE, version);
+{%- for field in struct.fields %}
+{%- if field.default %}
+ {{field|name}} = {{field|default_value}};
+{%- elif field.kind|is_any_handle_kind %}
+ {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE;
+{%- endif %}
+{%- endfor %}
+ }
+
+ public {{struct|name}}() {
+ this({{struct.versions[-1].version}});
+ }
+
+ public static {{struct|name}} deserialize(org.chromium.mojo.bindings.Message message) {
+ return decode(new org.chromium.mojo.bindings.Decoder(message));
+ }
+
+ /**
+ * Similar to the method above, but deserializes from a |ByteBuffer| instance.
+ *
+ * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure.
+ */
+ public static {{struct|name}} deserialize(java.nio.ByteBuffer data) {
+ if (data == null)
+ return null;
+
+ return deserialize(new org.chromium.mojo.bindings.Message(
+ data, new java.util.ArrayList<org.chromium.mojo.system.Handle>()));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) {
+ if (decoder0 == null) {
+ return null;
+ }
+ decoder0.increaseStackDepth();
+ {{struct|name}} result;
+ try {
+ org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY);
+ result = new {{struct|name}}(mainDataHeader.elementsOrVersion);
+{%- for byte in struct.bytes %}
+{%- for packed_field in byte.packed_fields %}
+ if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) {
+ {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(16)}}
+ }
+{%- endfor %}
+{%- endfor %}
+ } finally {
+ decoder0.decreaseStackDepth();
+ }
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ @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 %}
+ }
+
+ /**
+ * @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;
+{%- if struct.fields|length %}
+ {{struct|name}} other = ({{struct|name}}) object;
+{%- for field in struct.fields %}
+ if ({{equality(field.kind, 'this.'~field|name, 'other.'~field|name, True)}})
+ return false;
+{%- endfor %}
+{%- endif %}
+ return true;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = prime + getClass().hashCode();
+{%- for field in struct.fields %}
+ result = prime * result + {{hash(field.kind, field|name)}};
+{%- endfor %}
+ return result;
+ }
+}
+{%- endmacro %}
+
+
+{%- macro union_def(union) %}
+public final class {{union|name}} extends org.chromium.mojo.bindings.Union {
+
+ public static final class Tag {
+{%- for field in union.fields %}
+ public static final int {{field|ucc}} = {{loop.index0}};
+{%- endfor %}
+ };
+
+ private int mTag_ = -1;
+{%- for field in union.fields %}
+ private {{field.kind|java_type}} m{{field|ucc}};
+{%- endfor %}
+
+ public int which() {
+ return mTag_;
+ }
+
+ public boolean isUnknown() {
+ return mTag_ == -1;
+ }
+{%- for field in union.fields %}
+
+ // TODO(rockot): Fix the findbugs error and remove this suppression.
+ // See http://crbug.com/570386.
+ @SuppressFBWarnings("EI_EXPOSE_REP2")
+ public void set{{field|ucc}}({{field.kind|java_type}} {{field|name}}) {
+ mTag_ = Tag.{{field|ucc}};
+ m{{field|ucc}} = {{field|name}};
+ }
+
+ // TODO(rockot): Fix the findbugs error and remove this suppression.
+ // See http://crbug.com/570386.
+ @SuppressFBWarnings("EI_EXPOSE_REP")
+ public {{field.kind|java_type}} get{{field|ucc}}() {
+ assert mTag_ == Tag.{{field|ucc}};
+ return m{{field|ucc}};
+ }
+{%- endfor %}
+
+
+ @Override
+ protected final void encode(org.chromium.mojo.bindings.Encoder encoder0, int offset) {
+ encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset);
+ encoder0.encode(mTag_, offset + 4);
+ switch (mTag_) {
+{%- for field in union.fields %}
+ case Tag.{{field|ucc}}: {
+{%- if field.kind|is_union_kind %}
+ if (m{{field|ucc}} == null) {
+ encoder0.encodeNullPointer(offset + 8, {{field.kind|is_nullable_kind|java_true_false}});
+ } else {
+ m{{field|ucc}}.encode(encoder0.encoderForUnionPointer(offset + 8), 0);
+ }
+{%- else %}
+ {{encode('m' ~ field|ucc, field.kind, 'offset + 8', 0)|indent(16)}}
+{%- endif %}
+ break;
+ }
+{%- endfor %}
+ default: {
+ break;
+ }
+ }
+ }
+
+ public static {{union|name}} deserialize(org.chromium.mojo.bindings.Message message) {
+ return decode(new org.chromium.mojo.bindings.Decoder(message).decoderForSerializedUnion(), 0);
+ }
+
+ public static final {{union|name}} decode(org.chromium.mojo.bindings.Decoder decoder0, int offset) {
+ org.chromium.mojo.bindings.DataHeader dataHeader = decoder0.readDataHeaderForUnion(offset);
+ if (dataHeader.size == 0) {
+ return null;
+ }
+ {{union|name}} result = new {{union|name}}();
+ switch (dataHeader.elementsOrVersion) {
+{%- for field in union.fields %}
+ case Tag.{{field|ucc}}: {
+{%- if field.kind|is_union_kind %}
+ org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, {{field.kind|is_nullable_kind|java_true_false}});
+ if (decoder1 != null) {
+ result.m{{field|ucc}} = {{field.kind|name}}.decode(decoder1, 0);
+ }
+{%- else %}
+ {{decode('result.m'~field|ucc, field.kind, 'offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0)|indent(16)}}
+{%- endif %}
+ result.mTag_ = Tag.{{field|ucc}};
+ break;
+ }
+{%- endfor %}
+ default: {
+ break;
+ }
+ }
+ 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;
+ {{union|name}} other = ({{union|name}}) object;
+ if (mTag_ != other.mTag_)
+ return false;
+ switch (mTag_) {
+{%- for field in union.fields %}
+ case Tag.{{field|ucc}}:
+ return {{equality(field.kind, 'm'~field|ucc, 'other.m'~field|ucc)}};
+{%- endfor %}
+ default:
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = prime + getClass().hashCode();
+ result = prime * result + org.chromium.mojo.bindings.BindingsHelper.hashCode(mTag_);
+ switch (mTag_) {
+{%- for field in union.fields %}
+ case Tag.{{field|ucc}}: {
+ result = prime * result + {{hash(field.kind, 'm'~field|ucc)}};
+ break;
+ }
+{%- endfor %}
+ default: {
+ break;
+ }
+ }
+ return result;
+ }
+}
+{%- endmacro %}
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 0000000000..7096a18747
--- /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 0000000000..d37288ac5d
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
@@ -0,0 +1,42 @@
+{%- 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 static final boolean IS_EXTENSIBLE = {% if enum.extensible %}true{% else %}false{% endif %};
+
+ public static boolean isKnownValue(int value) {
+{%- if enum.fields %}
+ switch (value) {
+{%- for enum_field in enum.fields|groupby('numeric_value') %}
+ case {{enum_field[0]}}:
+{%- endfor %}
+ return true;
+ }
+{%- endif %}
+ return false;
+ }
+
+ public static void validate(int value) {
+ if (IS_EXTENSIBLE || isKnownValue(value))
+ return;
+
+ throw new DeserializationException("Invalid enum value.");
+ }
+
+ 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 0000000000..1d67890452
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
@@ -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.
+
+// This file is autogenerated by:
+// mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+// {{module.path}}
+//
+
+package {{package}};
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.mojo.bindings.DeserializationException; \ No newline at end of file
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 0000000000..a13be3ef60
--- /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) }}
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 0000000000..a723f8c393
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
@@ -0,0 +1,297 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
+{% from "data_types_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 flags_for_method(method, is_request) -%}
+{{flags(method.response_parameters != None, is_request)}}
+{%- endmacro -%}
+
+{%- macro flags(use_response_flag, is_request) -%}
+{%- if not use_response_flag -%}
+org.chromium.mojo.bindings.MessageHeader.NO_FLAG
+{%- elif is_request: -%}
+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, fully_qualified=False) -%}
+{% if fully_qualified %}org.chromium.mojo.bindings.Interface.{% endif %}Manager<{{interface|name}}, {{interface|name}}.Proxy>
+{%- endmacro -%}
+
+{%- macro manager_def(interface) -%}
+public static final {{manager_class(interface, True)}} MANAGER =
+ new {{manager_class(interface, True)}}() {
+
+ public String getName() {
+ return "{{namespace|replace(".","::")}}::{{interface.name}}";
+ }
+
+ public int getVersion() {
+ return {{interface.version}};
+ }
+
+ 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];
+ }
+};
+{%- endmacro -%}
+
+{%- macro accept_body(interface, 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()) {
+{% if with_response %}
+ case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID:
+ return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun(
+ getCore(), {{interface|name}}_Internal.MANAGER, messageWithHeader, receiver);
+{% else %}
+ case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID:
+ return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe(
+ {{interface|name}}_Internal.MANAGER, messageWithHeader);
+{% endif %}
+{% 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.param_struct %}
+{% if with_response %}
+{% set response_struct = method.response_param_struct %}
+{% 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 %}
+ try {
+ 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 %});
+ } catch (RuntimeException e) {
+ // TODO(lhchavez): Remove this hack. See b/28814913 for details.
+ android.util.Log.wtf("{{namespace}}.{{interface.name}}", "Uncaught runtime exception", e);
+ }
+ return true;
+ }
+{% endif %}
+{% endfor %}
+ default:
+ return false;
+ }
+} catch (org.chromium.mojo.bindings.DeserializationException e) {
+ System.err.println(e.toString());
+ return false;
+}
+{%- endmacro -%}
+
+{% macro interface_def(interface) %}
+public interface {{interface|name}} extends org.chromium.mojo.bindings.Interface {
+{% 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}}, org.chromium.mojo.bindings.Interface.Proxy {
+ }
+
+ {{manager_class(interface)}} 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) %}
+class {{interface|name}}_Internal {
+
+ {{manager_def(interface)|indent(4)}}
+
+{% for method in interface.methods %}
+ private static final int {{method|method_ordinal_name}} = {{method.ordinal}};
+{% endfor %}
+
+ static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy 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.param_struct %}
+ {{request_struct|name}} _message = new {{request_struct|name}}();
+{% for param in method.parameters %}
+ _message.{{param|name}} = {{param|name}};
+{% endfor %}
+{% if method.response_parameters != None %}
+ getProxyHandler().getMessageReceiver().acceptWithResponder(
+ _message.serializeWithHeader(
+ getProxyHandler().getCore(),
+ new org.chromium.mojo.bindings.MessageHeader(
+ {{method|method_ordinal_name}},
+ {{flags_for_method(method, True)}},
+ 0)),
+ new {{method.response_param_struct|name}}ForwardToCallback(callback));
+{% else %}
+ getProxyHandler().getMessageReceiver().accept(
+ _message.serializeWithHeader(
+ getProxyHandler().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.param_struct, True)|indent(4) }}
+{% if method.response_parameters != None %}
+{% set response_struct = method.response_param_struct %}
+
+ {{ 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 %}
+ try {
+ mCallback.call({{run_callback('response', method.response_parameters)}});
+ } catch (RuntimeException e) {
+ // TODO(lhchavez): Remove this hack. See b/28814913 for details.
+ android.util.Log.wtf("{{namespace}}.{{interface.name}}", "Uncaught runtime exception", e);
+ }
+ 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 0000000000..50c7a7bf94
--- /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) }}
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 0000000000..e28ba19c8b
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/struct.java.tmpl
@@ -0,0 +1,4 @@
+{% from "data_types_definition.tmpl" import struct_def %}
+{% include "header.java.tmpl" %}
+
+{{ struct_def(struct) }}
diff --git a/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl b/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl
new file mode 100644
index 0000000000..b8cd4aa2e0
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/java_templates/union.java.tmpl
@@ -0,0 +1,4 @@
+{% from "data_types_definition.tmpl" import union_def %}
+{% include "header.java.tmpl" %}
+
+{{ union_def(union) }}
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 0000000000..019b1b6383
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -0,0 +1,33 @@
+{%- macro enum_def(enum_name, enum) -%}
+ {{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 %}
+
+ {{enum_name}}.isKnownEnumValue = function(value) {
+{%- if enum.fields %}
+ switch (value) {
+{%- for enum_field in enum.fields|groupby('numeric_value') %}
+ case {{enum_field[0]}}:
+{%- endfor %}
+ return true;
+ }
+{%- endif %}
+ return false;
+ };
+
+ {{enum_name}}.validate = function(enumValue) {
+ var isExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
+ if (isExtensible || this.isKnownEnumValue(enumValue))
+ return validator.validationError.NONE;
+
+ return validator.validationError.UNKNOWN_ENUM_VALUE;
+ };
+{%- 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 0000000000..11e319c1f7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -0,0 +1,198 @@
+{%- for method in interface.methods %}
+ var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
+{%- endfor %}
+
+ function {{interface.name}}Ptr(handleOrPtrInfo) {
+ this.ptr = new bindings.InterfacePtrController({{interface.name}},
+ handleOrPtrInfo);
+ }
+
+ function {{interface.name}}Proxy(receiver) {
+ this.receiver_ = receiver;
+ }
+
+{%- for method in interface.methods %}
+ {{interface.name}}Ptr.prototype.{{method.name|stylize_method}} = function() {
+ return {{interface.name}}Proxy.prototype.{{method.name|stylize_method}}
+ .apply(this.ptr.getProxy(), arguments);
+ };
+
+ {{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_.acceptAndExpectResponse(message).then(function(message) {
+ var reader = new codec.MessageReader(message);
+ var responseParams =
+ reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
+ resolve(responseParams);
+ }).catch(function(result) {
+ reject(Error("Connection error: " + result));
+ });
+ }.bind(this));
+{%- endif %}
+ };
+{%- endfor %}
+
+ function {{interface.name}}Stub(delegate) {
+ this.delegate_ = delegate;
+ }
+
+{%- for method in interface.methods %}
+{%- set js_method_name = method.name|stylize_method %}
+ {{interface.name}}Stub.prototype.{{js_method_name}} = function({{method.parameters|map(attribute='name')|join(', ')}}) {
+ return this.delegate_ && this.delegate_.{{js_method_name}} && this.delegate_.{{js_method_name}}({{method.parameters|map(attribute='name')|join(', ')}});
+ }
+{%- endfor %}
+
+ {{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);
+ 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);
+ });
+ return true;
+{%- endif %}
+{%- endfor %}
+ default:
+ return false;
+ }
+ };
+
+{#--- 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 %}
+ }
+
+ var {{interface.name}} = {
+ name: '{{namespace|replace(".","::")}}::{{interface.name}}',
+ kVersion: {{interface.version}},
+ ptrClass: {{interface.name}}Ptr,
+ proxyClass: {{interface.name}}Proxy,
+ stubClass: {{interface.name}}Stub,
+ validateRequest: validate{{interface.name}}Request,
+{%- if interface|has_callbacks %}
+ validateResponse: validate{{interface.name}}Response,
+{%- else %}
+ validateResponse: null,
+{%- endif %}
+ };
+{#--- Interface Constants #}
+{%- for constant in interface.constants %}
+ {{interface.name}}.{{constant.name}} = {{constant.value|expression_to_text}},
+{%- endfor %}
+{#--- Interface Enums #}
+{%- from "enum_definition.tmpl" import enum_def -%}
+{%- for enum in interface.enums %}
+ {{ enum_def("%s.%s"|format(interface.name, enum.name), enum) }}
+{%- endfor %}
+ {{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 %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
new file mode 100644
index 0000000000..3637b196ac
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
@@ -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.
+
+{%- if use_new_js_bindings %}
+
+'use strict';
+
+(function() {
+ var mojomId = '{{module.path}}';
+ if (mojo.internal.isMojomLoaded(mojomId)) {
+ console.warn('The following mojom is loaded multiple times: ' + mojomId);
+ return;
+ }
+ mojo.internal.markMojomLoaded(mojomId);
+
+ // TODO(yzshen): Define these aliases to minimize the differences between the
+ // old/new modes. Remove them when the old mode goes away.
+ var bindings = mojo;
+ var codec = mojo.internal;
+ var validator = mojo.internal;
+
+{%- for import in imports %}
+ var {{import.unique_name}} =
+ mojo.internal.exposeNamespace('{{import.module.namespace}}');
+ if (mojo.config.autoLoadMojomDeps) {
+ mojo.internal.loadMojomIfNecessary(
+ '{{import.module.path}}',
+ new URL(
+ '{{import.module|get_relative_path(module)}}.js',
+ document.currentScript.src).href);
+ }
+{%- endfor %}
+
+{% include "module_definition.tmpl" %}
+})();
+
+{%- else %}
+
+define("{{module.path}}", [
+{%- if module.path !=
+ "mojo/public/interfaces/bindings/interface_control_messages.mojom" and
+ module.path !=
+ "mojo/public/interfaces/bindings/pipe_control_messages.mojom" %}
+ "mojo/public/js/bindings",
+{%- endif %}
+ "mojo/public/js/codec",
+ "mojo/public/js/core",
+ "mojo/public/js/validator",
+{%- for import in imports %}
+ "{{import.module.path}}",
+{%- endfor %}
+], function(
+{%- if module.path !=
+ "mojo/public/interfaces/bindings/interface_control_messages.mojom" and
+ module.path !=
+ "mojo/public/interfaces/bindings/pipe_control_messages.mojom" -%}
+bindings, {% endif -%}
+codec, core, validator
+{%- for import in imports -%}
+ , {{import.unique_name}}
+{%- endfor -%}
+) {
+
+{%- include "module_definition.tmpl" %}
+
+ return exports;
+});
+
+{%- endif %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
new file mode 100644
index 0000000000..a119ee9480
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
@@ -0,0 +1,49 @@
+{#--- 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) }}
+{%- endfor %}
+
+{#--- Struct definitions #}
+{% for struct in structs %}
+{%- include "struct_definition.tmpl" %}
+{%- endfor -%}
+
+{#--- Union definitions #}
+{%- from "union_definition.tmpl" import union_def %}
+{%- for union in unions %}
+{{union_def(union)|indent(2)}}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces -%}
+{%- include "interface_definition.tmpl" %}
+{%- endfor %}
+
+{%- if use_new_js_bindings %}
+ var exports = mojo.internal.exposeNamespace("{{module.namespace}}");
+{%- else %}
+ var exports = {};
+{%- endif %}
+
+{%- 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 union in unions %}
+ exports.{{union.name}} = {{union.name}};
+{%- endfor %}
+{%- for interface in interfaces %}
+ exports.{{interface.name}} = {{interface.name}};
+ exports.{{interface.name}}Ptr = {{interface.name}}Ptr;
+{%- endfor %}
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 0000000000..e823e46155
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -0,0 +1,126 @@
+{#--- 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)}}
+{%- 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;
+ err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+ if (err !== validator.validationError.NONE)
+ return err;
+
+ var kVersionSizes = [
+{%- for version in struct.versions %}
+ {version: {{version.version}}, numBytes: {{version.num_bytes}}}{% if not loop.last %},
+ {%- endif -%}
+{% endfor %}
+ ];
+ err = messageValidator.validateStructVersion(offset, kVersionSizes);
+ if (err !== validator.validationError.NONE)
+ return err;
+
+{#- Before validating fields introduced at a certain version, we need to add
+ a version check, which makes sure we skip further validation if |object|
+ is from an earlier version. |last_checked_version| records the last
+ version that we have added such version check. #}
+{%- from "validation_macros.tmpl" import validate_struct_field %}
+{%- set last_checked_version = 0 %}
+{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
+{%- set offset = packed_field|field_offset %}
+{%- set field = packed_field.field %}
+{%- set name = struct.name ~ '.' ~ field.name %}
+{% if field|is_object_field or field|is_any_handle_or_interface_field or
+ field|is_enum_field %}
+{% if packed_field.min_version > last_checked_version %}
+{% set last_checked_version = packed_field.min_version %}
+ // version check {{name}}
+ if (!messageValidator.isFieldInStructVersion(offset, {{packed_field.min_version}}))
+ return validator.validationError.NONE;
+{%- endif -%}
+{{validate_struct_field(field, offset, name)|indent(4)}}
+{%- 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 version = decoder.readUint32();
+{%- for byte in struct.bytes %}
+{%- if byte.packed_fields|length >= 1 and
+ byte.packed_fields[0].field|is_bool_field %}
+ 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.versions[-1].version}});
+
+{%- for byte in struct.bytes %}
+{%- if byte.packed_fields|length >= 1 and
+ byte.packed_fields[0].field|is_bool_field %}
+ 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/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
new file mode 100644
index 0000000000..4823febeca
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -0,0 +1,154 @@
+{%- macro union_def(union) %}
+function {{union.name}}(value) {
+ this.initDefault_();
+ this.initValue_(value);
+}
+
+{{tags(union)}}
+
+{{union.name}}.prototype.initDefault_ = function() {
+ this.$data = null;
+ this.$tag = undefined;
+}
+
+{{union.name}}.prototype.initValue_ = function(value) {
+ if (value == undefined) {
+ return;
+ }
+
+ var keys = Object.keys(value);
+ if (keys.length == 0) {
+ return;
+ }
+
+ if (keys.length > 1) {
+ throw new TypeError("You may set only one member on a union.");
+ }
+
+ var fields = [
+{%- for field in union.fields %}
+ "{{field.name}}",
+{%- endfor %}
+ ];
+
+ if (fields.indexOf(keys[0]) < 0) {
+ throw new ReferenceError(keys[0] + " is not a {{union.name}} member.");
+
+ }
+
+ this[keys[0]] = value[keys[0]];
+}
+
+{%- for field in union.fields %}
+Object.defineProperty({{union.name}}.prototype, "{{field.name}}", {
+ get: function() {
+ if (this.$tag != {{union.name}}.Tags.{{field.name}}) {
+ throw new ReferenceError(
+ "{{union.name}}.{{field.name}} is not currently set.");
+ }
+ return this.$data;
+ },
+
+ set: function(value) {
+ this.$tag = {{union.name}}.Tags.{{field.name}};
+ this.$data = value;
+ }
+});
+{%- endfor %}
+
+{{encode(union)|indent(2)}}
+
+{{decode(union)|indent(2)}}
+
+{{validate(union)|indent(2)}}
+
+{{union.name}}.encodedSize = 16;
+{%- endmacro %}
+
+{%- macro tags(union) %}
+{{union.name}}.Tags = {
+{%- for field in union.fields %}
+ {{field.name}}: {{field.ordinal}},
+{%- endfor %}
+};
+{%- endmacro %}
+
+{%- macro encode(union) %}
+{{union.name}}.encode = function(encoder, val) {
+ if (val == null) {
+ encoder.writeUint64(0);
+ encoder.writeUint64(0);
+ return;
+ }
+ if (val.$tag == undefined) {
+ throw new TypeError("Cannot encode unions with an unknown member set.");
+ }
+
+ encoder.writeUint32(16);
+ encoder.writeUint32(val.$tag);
+ switch (val.$tag) {
+{%- for field in union.fields %}
+ case {{union.name}}.Tags.{{field.name}}:
+{%- if field|is_bool_field %}
+ encoder.writeUint8(val.{{field.name}} ? 1 : 0);
+{%- else %}
+ encoder.{{field.kind|union_encode_snippet}}val.{{field.name}});
+{%- endif %}
+ break;
+{%- endfor %}
+ }
+ encoder.align();
+};
+{%- endmacro %}
+
+{%- macro decode(union) %}
+{{union.name}}.decode = function(decoder) {
+ var size = decoder.readUint32();
+ if (size == 0) {
+ decoder.readUint32();
+ decoder.readUint64();
+ return null;
+ }
+
+ var result = new {{union.name}}();
+ var tag = decoder.readUint32();
+ switch (tag) {
+{%- for field in union.fields %}
+ case {{union.name}}.Tags.{{field.name}}:
+{%- if field|is_bool_field %}
+ result.{{field.name}} = decoder.readUint8() ? true : false;
+{%- else %}
+ result.{{field.name}} = decoder.{{field.kind|union_decode_snippet}};
+{%- endif %}
+ break;
+{%- endfor %}
+ }
+ decoder.align();
+
+ return result;
+};
+{%- endmacro %}
+
+{%- from "validation_macros.tmpl" import validate_union_field %}
+{%- macro validate(union) %}
+{{union.name}}.validate = function(messageValidator, offset) {
+ var size = messageValidator.decodeUnionSize(offset);
+ if (size != 16) {
+ return validator.validationError.INVALID_UNION_SIZE;
+ }
+
+ var tag = messageValidator.decodeUnionTag(offset);
+ var data_offset = offset + 8;
+ var err;
+ switch (tag) {
+{%- for field in union.fields %}
+{%- set name = union.name ~ '.' ~ field.name %}
+ case {{union.name}}.Tags.{{field.name}}:
+ {{validate_union_field(field, "data_offset", name)}}
+ break;
+{%- endfor %}
+ }
+
+ return validator.validationError.NONE;
+};
+{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
new file mode 100644
index 0000000000..d4e15a7859
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
@@ -0,0 +1,60 @@
+{% macro _check_err() -%}
+if (err !== validator.validationError.NONE)
+ return err;
+{%- endmacro %}
+
+{%- macro _validate_field(field, offset, name) %}
+{%- if field|is_string_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateStringPointer({{offset}}, {{field|validate_nullable_params}})
+{{_check_err()}}
+{%- elif field|is_array_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateArrayPointer({{offset}}, {{field|validate_array_params}});
+{{_check_err()}}
+{%- elif field|is_struct_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateStructPointer({{offset}}, {{field|validate_struct_params}});
+{{_check_err()}}
+{%- elif field|is_map_pointer_field %}
+// validate {{name}}
+err = messageValidator.validateMapPointer({{offset}}, {{field|validate_map_params}});
+{{_check_err()}}
+{%- elif field|is_interface_field %}
+// validate {{name}}
+err = messageValidator.validateInterface({{offset}}, {{field|validate_nullable_params}});
+{{_check_err()}}
+{%- elif field|is_interface_request_field %}
+// validate {{name}}
+err = messageValidator.validateInterfaceRequest({{offset}}, {{field|validate_nullable_params}})
+{{_check_err()}}
+{%- elif field|is_handle_field %}
+// validate {{name}}
+err = messageValidator.validateHandle({{offset}}, {{field|validate_nullable_params}})
+{{_check_err()}}
+{%- elif field|is_enum_field %}
+// validate {{name}}
+err = messageValidator.validateEnum({{offset}}, {{field|validate_enum_params}});
+{{_check_err()}}
+{%- endif %}
+{%- endmacro %}
+
+{%- macro validate_struct_field(field, offset, name) %}
+{%- if field|is_union_field %}
+// validate {{name}}
+err = messageValidator.validateUnion({{offset}}, {{field|validate_union_params}});
+{{_check_err()}}
+{%- else %}
+{{_validate_field(field, offset, name)}}
+{%- endif %}
+{%- endmacro %}
+
+{%- macro validate_union_field(field, offset, name) %}
+{%- if field|is_union_field %}
+// validate {{name}}
+err = messageValidator.validateNestedUnion({{offset}}, {{field|validate_union_params}});
+{{_check_err()}}
+{%- else %}
+{{_validate_field(field, offset, name)}}
+{%- endif %}
+{%- endmacro %}
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 0000000000..38d222b136
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -0,0 +1,818 @@
+# 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.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",
+}
+
+# TODO(rockot): Get rid of these globals. This requires some refactoring of the
+# generator library code so that filters can use the generator as context.
+_current_typemap = {}
+_for_blink = False
+# TODO(rockot, yzshen): The variant handling is kind of a hack currently. Make
+# it right.
+_variant = None
+_export_attribute = None
+
+
+class _NameFormatter(object):
+ """A formatter for the names of kinds or values."""
+
+ def __init__(self, token, variant):
+ self._token = token
+ self._variant = variant
+
+ def Format(self, separator, prefixed=False, internal=False,
+ include_variant=False, add_same_module_namespaces=False,
+ flatten_nested_kind=False):
+ """Formats the name according to the given configuration.
+
+ Args:
+ separator: Separator between different parts of the name.
+ prefixed: Whether a leading separator should be added.
+ internal: Returns the name in the "internal" namespace.
+ include_variant: Whether to include variant as namespace. If |internal| is
+ True, then this flag is ignored and variant is not included.
+ add_same_module_namespaces: Includes all namespaces even if the token is
+ from the same module as the current mojom file.
+ flatten_nested_kind: It is allowed to define enums inside structs and
+ interfaces. If this flag is set to True, this method concatenates the
+ parent kind and the nested kind with '_', instead of treating the
+ parent kind as a scope."""
+
+ parts = []
+ if self._ShouldIncludeNamespace(add_same_module_namespaces):
+ if prefixed:
+ parts.append("")
+ parts.extend(self._GetNamespace())
+ if include_variant and self._variant and not internal:
+ parts.append(self._variant)
+ parts.extend(self._GetName(internal, flatten_nested_kind))
+ return separator.join(parts)
+
+ def FormatForCpp(self, add_same_module_namespaces=False, internal=False,
+ flatten_nested_kind=False):
+ return self.Format(
+ "::", prefixed=True,
+ add_same_module_namespaces=add_same_module_namespaces,
+ internal=internal, include_variant=True,
+ flatten_nested_kind=flatten_nested_kind)
+
+ def FormatForMojom(self):
+ return self.Format(".", add_same_module_namespaces=True)
+
+ def _MapKindName(self, token, internal):
+ if not internal:
+ return token.name
+ if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or
+ mojom.IsEnumKind(token)):
+ return token.name + "_Data"
+ return token.name
+
+ def _GetName(self, internal, flatten_nested_kind):
+ if isinstance(self._token, mojom.EnumValue):
+ name_parts = _NameFormatter(self._token.enum, self._variant)._GetName(
+ internal, flatten_nested_kind)
+ name_parts.append(self._token.name)
+ return name_parts
+
+ name_parts = []
+ if internal:
+ name_parts.append("internal")
+
+ if (flatten_nested_kind and mojom.IsEnumKind(self._token) and
+ self._token.parent_kind):
+ name = "%s_%s" % (self._token.parent_kind.name,
+ self._MapKindName(self._token, internal))
+ name_parts.append(name)
+ return name_parts
+
+ if self._token.parent_kind:
+ name_parts.append(self._MapKindName(self._token.parent_kind, internal))
+ name_parts.append(self._MapKindName(self._token, internal))
+ return name_parts
+
+ def _ShouldIncludeNamespace(self, add_same_module_namespaces):
+ return add_same_module_namespaces or self._token.imported_from
+
+ def _GetNamespace(self):
+ if self._token.imported_from:
+ return NamespaceToArray(self._token.imported_from["namespace"])
+ elif hasattr(self._token, "module"):
+ return NamespaceToArray(self._token.module.namespace)
+ return []
+
+
+def ConstantValue(constant):
+ return ExpressionToText(constant.value, kind=constant.kind)
+
+# TODO(yzshen): Revisit the default value feature. It was designed prior to
+# custom type mapping.
+def DefaultValue(field):
+ if field.default:
+ if mojom.IsStructKind(field.kind):
+ assert field.default == "default"
+ if not IsTypemappedKind(field.kind):
+ 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, flatten_nested_kind=False,
+ add_same_module_namespaces=False):
+ return _NameFormatter(kind, _variant).FormatForCpp(
+ internal=internal, flatten_nested_kind=flatten_nested_kind,
+ add_same_module_namespaces=add_same_module_namespaces)
+
+def GetQualifiedNameForKind(kind, internal=False, flatten_nested_kind=False,
+ include_variant=True):
+ return _NameFormatter(
+ kind, _variant if include_variant else None).FormatForCpp(
+ internal=internal, add_same_module_namespaces=True,
+ flatten_nested_kind=flatten_nested_kind)
+
+
+def GetWtfHashFnNameForEnum(enum):
+ return _NameFormatter(
+ enum, None).Format("_", internal=True, add_same_module_namespaces=True,
+ flatten_nested_kind=True) + "HashFn"
+
+
+def GetFullMojomNameForKind(kind):
+ return _NameFormatter(kind, _variant).FormatForMojom()
+
+def IsTypemappedKind(kind):
+ return hasattr(kind, "name") and \
+ GetFullMojomNameForKind(kind) in _current_typemap
+
+def IsNativeOnlyKind(kind):
+ return (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)) and \
+ kind.native_only
+
+
+def IsHashableKind(kind):
+ """Check if the kind can be hashed.
+
+ Args:
+ kind: {Kind} The kind to check.
+
+ Returns:
+ {bool} True if a value of this kind can be hashed.
+ """
+ checked = set()
+ def Check(kind):
+ if kind.spec in checked:
+ return True
+ checked.add(kind.spec)
+ if mojom.IsNullableKind(kind):
+ return False
+ elif mojom.IsStructKind(kind):
+ if (IsTypemappedKind(kind) and
+ not _current_typemap[GetFullMojomNameForKind(kind)]["hashable"]):
+ return False
+ return all(Check(field.kind) for field in kind.fields)
+ elif mojom.IsEnumKind(kind):
+ return not IsTypemappedKind(kind) or _current_typemap[
+ GetFullMojomNameForKind(kind)]["hashable"]
+ elif mojom.IsUnionKind(kind):
+ return all(Check(field.kind) for field in kind.fields)
+ elif mojom.IsAnyHandleKind(kind):
+ return False
+ elif mojom.IsAnyInterfaceKind(kind):
+ return False
+ # TODO(tibell): Arrays and maps could be made hashable. We just don't have a
+ # use case yet.
+ elif mojom.IsArrayKind(kind):
+ return False
+ elif mojom.IsMapKind(kind):
+ return False
+ else:
+ return True
+ return Check(kind)
+
+
+def AllEnumValues(enum):
+ """Return all enum values associated with an enum.
+
+ Args:
+ enum: {mojom.Enum} The enum type.
+
+ Returns:
+ {Set[int]} The values.
+ """
+ return set(field.numeric_value for field in enum.fields)
+
+
+def GetNativeTypeName(typemapped_kind):
+ return _current_typemap[GetFullMojomNameForKind(typemapped_kind)]["typename"]
+
+def GetCppPodType(kind):
+ return _kind_to_cpp_type[kind]
+
+def FormatConstantDeclaration(constant, nested=False):
+ if mojom.IsStringKind(constant.kind):
+ if nested:
+ return "const char %s[]" % constant.name
+ return "%sextern const char %s[]" % \
+ ((_export_attribute + " ") if _export_attribute else "", constant.name)
+ return "constexpr %s %s = %s" % (GetCppPodType(constant.kind), constant.name,
+ ConstantValue(constant))
+
+def GetCppWrapperType(kind, add_same_module_namespaces=False):
+ def _AddOptional(type_name):
+ pattern = "WTF::Optional<%s>" if _for_blink else "base::Optional<%s>"
+ return pattern % type_name
+
+ if IsTypemappedKind(kind):
+ type_name = GetNativeTypeName(kind)
+ if (mojom.IsNullableKind(kind) and
+ not _current_typemap[GetFullMojomNameForKind(kind)][
+ "nullable_is_same_type"]):
+ type_name = _AddOptional(type_name)
+ return type_name
+ if mojom.IsEnumKind(kind):
+ return GetNameForKind(
+ kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+ return "%sPtr" % GetNameForKind(
+ kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsArrayKind(kind):
+ pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>"
+ if mojom.IsNullableKind(kind):
+ pattern = _AddOptional(pattern)
+ return pattern % GetCppWrapperType(
+ kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsMapKind(kind):
+ pattern = ("WTF::HashMap<%s, %s>" if _for_blink else
+ "std::unordered_map<%s, %s>")
+ if mojom.IsNullableKind(kind):
+ pattern = _AddOptional(pattern)
+ return pattern % (
+ GetCppWrapperType(
+ kind.key_kind,
+ add_same_module_namespaces=add_same_module_namespaces),
+ GetCppWrapperType(
+ kind.value_kind,
+ add_same_module_namespaces=add_same_module_namespaces))
+ if mojom.IsInterfaceKind(kind):
+ return "%sPtr" % GetNameForKind(
+ kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsInterfaceRequestKind(kind):
+ return "%sRequest" % GetNameForKind(
+ kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsAssociatedInterfaceKind(kind):
+ return "%sAssociatedPtrInfo" % GetNameForKind(
+ kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsAssociatedInterfaceRequestKind(kind):
+ return "%sAssociatedRequest" % GetNameForKind(
+ kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ if mojom.IsStringKind(kind):
+ if _for_blink:
+ return "WTF::String"
+ type_name = "std::string"
+ return _AddOptional(type_name) if mojom.IsNullableKind(kind) else type_name
+ if mojom.IsGenericHandleKind(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:
+ raise Exception("Unrecognized kind %s" % kind.spec)
+ return _kind_to_cpp_type[kind]
+
+def IsMoveOnlyKind(kind):
+ if IsTypemappedKind(kind):
+ if mojom.IsEnumKind(kind):
+ return False
+ return _current_typemap[GetFullMojomNameForKind(kind)]["move_only"]
+ if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+ return True
+ if mojom.IsArrayKind(kind):
+ return IsMoveOnlyKind(kind.kind)
+ if mojom.IsMapKind(kind):
+ return IsMoveOnlyKind(kind.value_kind)
+ if mojom.IsAnyHandleOrInterfaceKind(kind):
+ return True
+ return False
+
+def IsCopyablePassByValue(kind):
+ if not IsTypemappedKind(kind):
+ return False
+ return _current_typemap[GetFullMojomNameForKind(kind)][
+ "copyable_pass_by_value"]
+
+def ShouldPassParamByValue(kind):
+ return ((not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind) or
+ IsCopyablePassByValue(kind))
+
+def GetCppWrapperParamType(kind):
+ cpp_wrapper_type = GetCppWrapperType(kind)
+ return (cpp_wrapper_type if ShouldPassParamByValue(kind)
+ else "const %s&" % cpp_wrapper_type)
+
+def GetCppFieldType(kind):
+ if mojom.IsStructKind(kind):
+ return ("mojo::internal::Pointer<%s>" %
+ GetNameForKind(kind, internal=True))
+ if mojom.IsUnionKind(kind):
+ return "%s" % GetNameForKind(kind, internal=True)
+ if mojom.IsArrayKind(kind):
+ return ("mojo::internal::Pointer<mojo::internal::Array_Data<%s>>" %
+ GetCppFieldType(kind.kind))
+ if mojom.IsMapKind(kind):
+ return ("mojo::internal::Pointer<mojo::internal::Map_Data<%s, %s>>" %
+ (GetCppFieldType(kind.key_kind), GetCppFieldType(kind.value_kind)))
+ if mojom.IsInterfaceKind(kind):
+ return "mojo::internal::Interface_Data"
+ if mojom.IsInterfaceRequestKind(kind):
+ return "mojo::internal::Handle_Data"
+ if mojom.IsAssociatedInterfaceKind(kind):
+ return "mojo::internal::AssociatedInterface_Data"
+ if mojom.IsAssociatedInterfaceRequestKind(kind):
+ return "mojo::internal::AssociatedEndpointHandle_Data"
+ if mojom.IsEnumKind(kind):
+ return "int32_t"
+ if mojom.IsStringKind(kind):
+ return "mojo::internal::Pointer<mojo::internal::String_Data>"
+ if mojom.IsAnyHandleKind(kind):
+ return "mojo::internal::Handle_Data"
+ return _kind_to_cpp_type[kind]
+
+def GetCppUnionFieldType(kind):
+ if mojom.IsUnionKind(kind):
+ return ("mojo::internal::Pointer<%s>" % GetNameForKind(kind, internal=True))
+ return GetCppFieldType(kind)
+
+def GetUnionGetterReturnType(kind):
+ if mojom.IsReferenceKind(kind):
+ return "%s&" % GetCppWrapperType(kind)
+ return GetCppWrapperType(kind)
+
+def GetUnionTraitGetterReturnType(kind):
+ """Get field type used in UnionTraits template specialization.
+
+ The type may be qualified as UnionTraits specializations live outside the
+ namespace where e.g. structs are defined.
+
+ Args:
+ kind: {Kind} The type of the field.
+
+ Returns:
+ {str} The C++ type to use for the field.
+ """
+ if mojom.IsReferenceKind(kind):
+ return "%s&" % GetCppWrapperType(kind, add_same_module_namespaces=True)
+ return GetCppWrapperType(kind, add_same_module_namespaces=True)
+
+def GetCppDataViewType(kind, qualified=False):
+ def _GetName(input_kind):
+ return _NameFormatter(input_kind, None).FormatForCpp(
+ add_same_module_namespaces=qualified, flatten_nested_kind=True)
+
+ if mojom.IsEnumKind(kind):
+ return _GetName(kind)
+ if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+ return "%sDataView" % _GetName(kind)
+ if mojom.IsArrayKind(kind):
+ return "mojo::ArrayDataView<%s>" % GetCppDataViewType(kind.kind, qualified)
+ if mojom.IsMapKind(kind):
+ return ("mojo::MapDataView<%s, %s>" % (
+ GetCppDataViewType(kind.key_kind, qualified),
+ GetCppDataViewType(kind.value_kind, qualified)))
+ if mojom.IsStringKind(kind):
+ return "mojo::StringDataView"
+ if mojom.IsInterfaceKind(kind):
+ return "%sPtrDataView" % _GetName(kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return "%sRequestDataView" % _GetName(kind.kind)
+ if mojom.IsAssociatedInterfaceKind(kind):
+ return "%sAssociatedPtrInfoDataView" % _GetName(kind.kind)
+ if mojom.IsAssociatedInterfaceRequestKind(kind):
+ return "%sAssociatedRequestDataView" % _GetName(kind.kind)
+ if mojom.IsGenericHandleKind(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 GetUnmappedTypeForSerializer(kind):
+ return GetCppDataViewType(kind, qualified=True)
+
+def TranslateConstants(token, kind):
+ if isinstance(token, mojom.NamedValue):
+ return GetNameForKind(token, flatten_nested_kind=True)
+
+ if isinstance(token, mojom.BuiltinValue):
+ if token.value == "double.INFINITY":
+ return "std::numeric_limits<double>::infinity()"
+ if token.value == "float.INFINITY":
+ return "std::numeric_limits<float>::infinity()"
+ if token.value == "double.NEGATIVE_INFINITY":
+ return "-std::numeric_limits<double>::infinity()"
+ if token.value == "float.NEGATIVE_INFINITY":
+ return "-std::numeric_limits<float>::infinity()"
+ if token.value == "double.NAN":
+ return "std::numeric_limits<double>::quiet_NaN()"
+ if token.value == "float.NAN":
+ return "std::numeric_limits<float>::quiet_NaN()"
+
+ if (kind is not None and mojom.IsFloatKind(kind)):
+ return token if token.isdigit() else token + "f";
+
+ # Per C++11, 2.14.2, the type of an integer literal is the first of the
+ # corresponding list in Table 6 in which its value can be represented. In this
+ # case, the list for decimal constants with no suffix is:
+ # int, long int, long long int
+ # The standard considers a program ill-formed if it contains an integer
+ # literal that cannot be represented by any of the allowed types.
+ #
+ # As it turns out, MSVC doesn't bother trying to fall back to long long int,
+ # so the integral constant -2147483648 causes it grief: it decides to
+ # represent 2147483648 as an unsigned integer, and then warns that the unary
+ # minus operator doesn't make sense on unsigned types. Doh!
+ if kind == mojom.INT32 and token == "-2147483648":
+ return "(-%d - 1) /* %s */" % (
+ 2**31 - 1, "Workaround for MSVC bug; see https://crbug.com/445618")
+
+ return "%s%s" % (token, _kind_to_cpp_literal_suffix.get(kind, ""))
+
+def ExpressionToText(value, kind=None):
+ return TranslateConstants(value, kind)
+
+def RequiresContextForDataView(kind):
+ for field in kind.fields:
+ if mojom.IsReferenceKind(field.kind):
+ return True
+ return False
+
+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.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind):
+ return False
+ return True
+
+def ContainsMoveOnlyMembers(struct):
+ for field in struct.fields:
+ if IsMoveOnlyKind(field.kind):
+ return True
+ return False
+
+def ShouldInlineUnion(union):
+ return not any(
+ mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind)
+ for field in union.fields)
+
+
+class StructConstructor(object):
+ """Represents a constructor for a generated struct.
+
+ Fields:
+ fields: {[Field]} All struct fields in order.
+ params: {[Field]} The fields that are passed as params.
+ """
+
+ def __init__(self, fields, params):
+ self._fields = fields
+ self._params = set(params)
+
+ @property
+ def params(self):
+ return [field for field in self._fields if field in self._params]
+
+ @property
+ def fields(self):
+ for field in self._fields:
+ yield (field, field in self._params)
+
+
+def GetStructConstructors(struct):
+ """Returns a list of constructors for a struct.
+
+ Params:
+ struct: {Struct} The struct to return constructors for.
+
+ Returns:
+ {[StructConstructor]} A list of StructConstructors that should be generated
+ for |struct|.
+ """
+ if not mojom.IsStructKind(struct):
+ raise TypeError
+ # Types that are neither copyable nor movable can't be passed to a struct
+ # constructor so only generate a default constructor.
+ if any(IsTypemappedKind(field.kind) and _current_typemap[
+ GetFullMojomNameForKind(field.kind)]["non_copyable_non_movable"]
+ for field in struct.fields):
+ return [StructConstructor(struct.fields, [])]
+
+ param_counts = [0]
+ for version in struct.versions:
+ if param_counts[-1] != version.num_fields:
+ param_counts.append(version.num_fields)
+
+ ordinal_fields = sorted(struct.fields, key=lambda field: field.ordinal)
+ return (StructConstructor(struct.fields, ordinal_fields[:param_count])
+ for param_count in param_counts)
+
+
+def GetContainerValidateParamsCtorArgs(kind):
+ if mojom.IsStringKind(kind):
+ expected_num_elements = 0
+ element_is_nullable = False
+ key_validate_params = "nullptr"
+ element_validate_params = "nullptr"
+ enum_validate_func = "nullptr"
+ elif mojom.IsMapKind(kind):
+ expected_num_elements = 0
+ element_is_nullable = False
+ key_validate_params = GetNewContainerValidateParams(mojom.Array(
+ kind=kind.key_kind))
+ element_validate_params = GetNewContainerValidateParams(mojom.Array(
+ kind=kind.value_kind))
+ enum_validate_func = "nullptr"
+ else: # mojom.IsArrayKind(kind)
+ expected_num_elements = generator.ExpectedArraySize(kind) or 0
+ element_is_nullable = mojom.IsNullableKind(kind.kind)
+ key_validate_params = "nullptr"
+ element_validate_params = GetNewContainerValidateParams(kind.kind)
+ if mojom.IsEnumKind(kind.kind):
+ enum_validate_func = ("%s::Validate" %
+ GetQualifiedNameForKind(kind.kind, internal=True,
+ flatten_nested_kind=True))
+ else:
+ enum_validate_func = "nullptr"
+
+ if enum_validate_func == "nullptr":
+ if key_validate_params == "nullptr":
+ return "%d, %s, %s" % (expected_num_elements,
+ "true" if element_is_nullable else "false",
+ element_validate_params)
+ else:
+ return "%s, %s" % (key_validate_params, element_validate_params)
+ else:
+ return "%d, %s" % (expected_num_elements, enum_validate_func)
+
+def GetNewContainerValidateParams(kind):
+ if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and
+ not mojom.IsStringKind(kind)):
+ return "nullptr"
+
+ return "new mojo::internal::ContainerValidateParams(%s)" % (
+ GetContainerValidateParamsCtorArgs(kind))
+
+class Generator(generator.Generator):
+
+ cpp_filters = {
+ "all_enum_values": AllEnumValues,
+ "constant_value": ConstantValue,
+ "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces,
+ "contains_move_only_members": ContainsMoveOnlyMembers,
+ "cpp_wrapper_param_type": GetCppWrapperParamType,
+ "cpp_data_view_type": GetCppDataViewType,
+ "cpp_field_type": GetCppFieldType,
+ "cpp_union_field_type": GetCppUnionFieldType,
+ "cpp_pod_type": GetCppPodType,
+ "cpp_union_getter_return_type": GetUnionGetterReturnType,
+ "cpp_union_trait_getter_return_type": GetUnionTraitGetterReturnType,
+ "cpp_wrapper_type": GetCppWrapperType,
+ "default_value": DefaultValue,
+ "expression_to_text": ExpressionToText,
+ "format_constant_declaration": FormatConstantDeclaration,
+ "get_container_validate_params_ctor_args":
+ GetContainerValidateParamsCtorArgs,
+ "get_name_for_kind": GetNameForKind,
+ "get_pad": pack.GetPad,
+ "get_qualified_name_for_kind": GetQualifiedNameForKind,
+ "has_callbacks": mojom.HasCallbacks,
+ "has_sync_methods": mojom.HasSyncMethods,
+ "requires_context_for_data_view": RequiresContextForDataView,
+ "should_inline": ShouldInlineStruct,
+ "should_inline_union": ShouldInlineUnion,
+ "is_array_kind": mojom.IsArrayKind,
+ "is_enum_kind": mojom.IsEnumKind,
+ "is_integral_kind": mojom.IsIntegralKind,
+ "is_native_only_kind": IsNativeOnlyKind,
+ "is_any_handle_kind": mojom.IsAnyHandleKind,
+ "is_any_interface_kind": mojom.IsAnyInterfaceKind,
+ "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind,
+ "is_associated_kind": mojom.IsAssociatedKind,
+ "is_hashable": IsHashableKind,
+ "is_map_kind": mojom.IsMapKind,
+ "is_nullable_kind": mojom.IsNullableKind,
+ "is_object_kind": mojom.IsObjectKind,
+ "is_reference_kind": mojom.IsReferenceKind,
+ "is_string_kind": mojom.IsStringKind,
+ "is_struct_kind": mojom.IsStructKind,
+ "is_typemapped_kind": IsTypemappedKind,
+ "is_union_kind": mojom.IsUnionKind,
+ "passes_associated_kinds": mojom.PassesAssociatedKinds,
+ "struct_constructors": GetStructConstructors,
+ "stylize_method": generator.StudlyCapsToCamel,
+ "under_to_camel": generator.UnderToCamel,
+ "unmapped_type_for_serializer": GetUnmappedTypeForSerializer,
+ "wtf_hash_fn_name_for_enum": GetWtfHashFnNameForEnum,
+ }
+
+ def GetExtraTraitsHeaders(self):
+ extra_headers = set()
+ for typemap in self._GetAllUsedTypemaps():
+ extra_headers.update(typemap.get("traits_headers", []))
+ return sorted(extra_headers)
+
+ def _GetAllUsedTypemaps(self):
+ """Returns the typemaps for types needed for serialization in this module.
+
+ A type is needed for serialization if it is contained by a struct or union
+ defined in this module, is a parameter of a message in an interface in
+ this module or is contained within another type needed for serialization.
+ """
+ used_typemaps = []
+ seen_types = set()
+ def AddKind(kind):
+ if (mojom.IsIntegralKind(kind) or mojom.IsStringKind(kind) or
+ mojom.IsDoubleKind(kind) or mojom.IsFloatKind(kind) or
+ mojom.IsAnyHandleKind(kind) or
+ mojom.IsInterfaceKind(kind) or
+ mojom.IsInterfaceRequestKind(kind) or
+ mojom.IsAssociatedKind(kind)):
+ pass
+ elif mojom.IsArrayKind(kind):
+ AddKind(kind.kind)
+ elif mojom.IsMapKind(kind):
+ AddKind(kind.key_kind)
+ AddKind(kind.value_kind)
+ else:
+ name = GetFullMojomNameForKind(kind)
+ if name in seen_types:
+ return
+ seen_types.add(name)
+
+ typemap = _current_typemap.get(name, None)
+ if typemap:
+ used_typemaps.append(typemap)
+ if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
+ for field in kind.fields:
+ AddKind(field.kind)
+
+ for kind in self.module.structs + self.module.unions:
+ for field in kind.fields:
+ AddKind(field.kind)
+
+ for interface in self.module.interfaces:
+ for method in interface.methods:
+ for parameter in method.parameters + (method.response_parameters or []):
+ AddKind(parameter.kind)
+
+ return used_typemaps
+
+ def GetExtraPublicHeaders(self):
+ all_enums = list(self.module.enums)
+ for struct in self.module.structs:
+ all_enums.extend(struct.enums)
+ for interface in self.module.interfaces:
+ all_enums.extend(interface.enums)
+
+ types = set(GetFullMojomNameForKind(typename)
+ for typename in
+ self.module.structs + all_enums + self.module.unions)
+ headers = set()
+ for typename, typemap in self.typemap.iteritems():
+ if typename in types:
+ headers.update(typemap.get("public_headers", []))
+ return sorted(headers)
+
+ def _GetDirectlyUsedKinds(self):
+ for struct in self.module.structs + self.module.unions:
+ for field in struct.fields:
+ yield field.kind
+
+ for interface in self.module.interfaces:
+ for method in interface.methods:
+ for param in method.parameters + (method.response_parameters or []):
+ yield param.kind
+
+ def GetJinjaExports(self):
+ structs = self.GetStructs()
+ interfaces = self.GetInterfaces()
+ all_enums = list(self.module.enums)
+ for struct in structs:
+ all_enums.extend(struct.enums)
+ for interface in interfaces:
+ all_enums.extend(interface.enums)
+
+ 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,
+ "all_enums": all_enums,
+ "structs": structs,
+ "unions": self.GetUnions(),
+ "interfaces": interfaces,
+ "variant": self.variant,
+ "extra_traits_headers": self.GetExtraTraitsHeaders(),
+ "extra_public_headers": self.GetExtraPublicHeaders(),
+ "for_blink": self.for_blink,
+ "use_once_callback": self.use_once_callback,
+ "export_attribute": self.export_attribute,
+ "export_header": self.export_header,
+ }
+
+ @staticmethod
+ def GetTemplatePrefix():
+ return "cpp_templates"
+
+ @classmethod
+ def GetFilters(cls):
+ return cls.cpp_filters
+
+ @UseJinja("module.h.tmpl")
+ def GenerateModuleHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("module.cc.tmpl")
+ def GenerateModuleSource(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("module-shared.h.tmpl")
+ def GenerateModuleSharedHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("module-shared-internal.h.tmpl")
+ def GenerateModuleSharedInternalHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("module-shared.cc.tmpl")
+ def GenerateModuleSharedSource(self):
+ return self.GetJinjaExports()
+
+ def GenerateFiles(self, args):
+ if self.generate_non_variant_code:
+ self.Write(self.GenerateModuleSharedHeader(),
+ self.MatchMojomFilePath("%s-shared.h" % self.module.name))
+ self.Write(
+ self.GenerateModuleSharedInternalHeader(),
+ self.MatchMojomFilePath("%s-shared-internal.h" % self.module.name))
+ self.Write(self.GenerateModuleSharedSource(),
+ self.MatchMojomFilePath("%s-shared.cc" % self.module.name))
+ else:
+ global _current_typemap
+ _current_typemap = self.typemap
+ global _for_blink
+ _for_blink = self.for_blink
+ global _use_once_callback
+ _use_once_callback = self.use_once_callback
+ global _variant
+ _variant = self.variant
+ global _export_attribute
+ _export_attribute = self.export_attribute
+ suffix = "-%s" % self.variant if self.variant else ""
+ self.Write(self.GenerateModuleHeader(),
+ self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
+ self.Write(
+ self.GenerateModuleSource(),
+ self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
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 0000000000..c7657ff99a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -0,0 +1,550 @@
+# Copyright 2014 The Chromium 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 sys
+import tempfile
+
+from jinja2 import contextfilter
+
+import mojom.fileutil as fileutil
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir, os.pardir, os.pardir, os.pardir,
+ 'build', 'android', 'gyp'))
+from util import build_utils
+
+
+GENERATOR_PREFIX = 'java'
+
+_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) or mojom.IsUnionKind(element)):
+ return UpperCamelCase(element.name)
+ if mojom.IsInterfaceRequestKind(element) or mojom.IsAssociatedKind(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.IsArrayKind(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.IsArrayKind(kind):
+ params.append(GetArrayNullabilityFlags(kind))
+ else:
+ params.append(GetJavaTrueFalse(mojom.IsNullableKind(kind)))
+ if mojom.IsArrayKind(kind):
+ params.append(GetArrayExpectedLength(kind))
+ if mojom.IsInterfaceKind(kind):
+ params.append('%s.MANAGER' % GetJavaType(context, kind))
+ if mojom.IsArrayKind(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.IsArrayKind(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'
+ if mojom.IsAssociatedInterfaceRequestKind(kind):
+ return 'readAssociatedInterfaceRequestNotSupported'
+ if mojom.IsAssociatedInterfaceKind(kind):
+ return 'readAssociatedServiceInterfaceNotSupported'
+ 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 module.attributes and 'JavaPackage' in module.attributes:
+ return ParseStringAttribute(module.attributes['JavaPackage'])
+ # Default package.
+ if module.namespace:
+ return 'org.chromium.' + module.namespace
+ return 'org.chromium'
+
+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)
+
+@contextfilter
+def GetJavaClassForEnum(context, kind):
+ return GetNameForKind(context, kind)
+
+def GetBoxedJavaType(context, kind, with_generics=True):
+ unboxed_type = GetJavaType(context, kind, False, with_generics)
+ 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, with_generics=True):
+ if boxed:
+ return GetBoxedJavaType(context, kind)
+ if (mojom.IsStructKind(kind) or
+ mojom.IsInterfaceKind(kind) or
+ mojom.IsUnionKind(kind)):
+ return GetNameForKind(context, kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return ('org.chromium.mojo.bindings.InterfaceRequest<%s>' %
+ GetNameForKind(context, kind.kind))
+ if mojom.IsAssociatedInterfaceKind(kind):
+ return 'org.chromium.mojo.bindings.AssociatedInterfaceNotSupported'
+ if mojom.IsAssociatedInterfaceRequestKind(kind):
+ return 'org.chromium.mojo.bindings.AssociatedInterfaceRequestNotSupported'
+ if mojom.IsMapKind(kind):
+ if with_generics:
+ return 'java.util.Map<%s, %s>' % (
+ GetBoxedJavaType(context, kind.key_kind),
+ GetBoxedJavaType(context, kind.value_kind))
+ else:
+ return 'java.util.Map'
+ if mojom.IsArrayKind(kind):
+ return '%s[]' % GetJavaType(context, kind.kind, boxed, with_generics)
+ 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.IsArrayKind(kind.kind):
+ return NewArray(context, kind.kind, size) + '[]'
+ return 'new %s[%s]' % (
+ GetJavaType(context, kind.kind, boxed=False, with_generics=False), 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 GetArrayKind(kind, size = None):
+ if size is None:
+ return mojom.Array(kind)
+ else:
+ array = mojom.Array(kind, 0)
+ array.java_map_size = size
+ return array
+
+def GetArrayExpectedLength(kind):
+ if mojom.IsArrayKind(kind) and kind.length is not None:
+ return getattr(kind, 'java_map_size', str(kind.length))
+ else:
+ return 'org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH'
+
+def IsPointerArrayKind(kind):
+ if not mojom.IsArrayKind(kind):
+ return False
+ sub_kind = kind.kind
+ return mojom.IsObjectKind(sub_kind) and not mojom.IsUnionKind(sub_kind)
+
+def IsUnionArrayKind(kind):
+ if not mojom.IsArrayKind(kind):
+ return False
+ sub_kind = kind.kind
+ return mojom.IsUnionKind(sub_kind)
+
+def GetConstantsMainEntityName(module):
+ if module.attributes and '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)
+
+class Generator(generator.Generator):
+
+ java_filters = {
+ 'array_expected_length': GetArrayExpectedLength,
+ 'array': GetArrayKind,
+ 'constant_value': ConstantValue,
+ 'decode_method': DecodeMethod,
+ 'default_value': DefaultValue,
+ 'encode_method': EncodeMethod,
+ 'expression_to_text': ExpressionToText,
+ 'has_method_without_response': HasMethodWithoutResponse,
+ 'has_method_with_response': HasMethodWithResponse,
+ 'interface_response_name': GetInterfaceResponseName,
+ 'is_array_kind': mojom.IsArrayKind,
+ 'is_any_handle_kind': mojom.IsAnyHandleKind,
+ "is_enum_kind": mojom.IsEnumKind,
+ 'is_interface_request_kind': mojom.IsInterfaceRequestKind,
+ 'is_map_kind': mojom.IsMapKind,
+ 'is_nullable_kind': mojom.IsNullableKind,
+ 'is_pointer_array_kind': IsPointerArrayKind,
+ 'is_reference_kind': mojom.IsReferenceKind,
+ 'is_struct_kind': mojom.IsStructKind,
+ 'is_union_array_kind': IsUnionArrayKind,
+ 'is_union_kind': mojom.IsUnionKind,
+ 'java_class_for_enum': GetJavaClassForEnum,
+ 'java_true_false': GetJavaTrueFalse,
+ 'java_type': GetJavaType,
+ 'method_ordinal_name': GetMethodOrdinalName,
+ 'name': GetNameForElement,
+ 'new_array': NewArray,
+ 'ucc': lambda x: UpperCamelCase(x.name),
+ }
+
+ def GetJinjaExports(self):
+ return {
+ 'package': GetPackage(self.module),
+ }
+
+ @staticmethod
+ def GetTemplatePrefix():
+ return "java_templates"
+
+ @classmethod
+ def GetFilters(cls):
+ return cls.java_filters
+
+ def GetJinjaExportsForInterface(self, interface):
+ exports = self.GetJinjaExports()
+ exports.update({'interface': interface})
+ return exports
+
+ @UseJinja('enum.java.tmpl')
+ def GenerateEnumSource(self, enum):
+ exports = self.GetJinjaExports()
+ exports.update({'enum': enum})
+ return exports
+
+ @UseJinja('struct.java.tmpl')
+ def GenerateStructSource(self, struct):
+ exports = self.GetJinjaExports()
+ exports.update({'struct': struct})
+ return exports
+
+ @UseJinja('union.java.tmpl')
+ def GenerateUnionSource(self, union):
+ exports = self.GetJinjaExports()
+ exports.update({'union': union})
+ return exports
+
+ @UseJinja('interface.java.tmpl')
+ def GenerateInterfaceSource(self, interface):
+ return self.GetJinjaExportsForInterface(interface)
+
+ @UseJinja('interface_internal.java.tmpl')
+ def GenerateInterfaceInternalSource(self, interface):
+ return self.GetJinjaExportsForInterface(interface)
+
+ @UseJinja('constants.java.tmpl')
+ def GenerateConstantsSource(self, module):
+ exports = self.GetJinjaExports()
+ exports.update({'main_entity': GetConstantsMainEntityName(module),
+ 'constants': module.constants})
+ return exports
+
+ def DoGenerateFiles(self):
+ fileutil.EnsureDirectoryExists(self.output_dir)
+
+ # 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 union in self.module.unions:
+ self.Write(self.GenerateUnionSource(union),
+ '%s.java' % GetNameForElement(union))
+
+ for enum in self.module.enums:
+ self.Write(self.GenerateEnumSource(enum),
+ '%s.java' % GetNameForElement(enum))
+
+ for interface in self.GetInterfaces():
+ 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):
+ # TODO(rockot): Support variant output for Java.
+ if self.variant:
+ raise Exception("Variants not supported in Java bindings.")
+
+ 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.
+ basename = self.MatchMojomFilePath("%s.srcjar" % self.module.name)
+ zip_filename = os.path.join(self.output_dir, basename)
+ with TempDir() as temp_java_root:
+ self.output_dir = os.path.join(temp_java_root, package_path)
+ self.DoGenerateFiles();
+ build_utils.ZipDir(zip_filename, temp_java_root)
+
+ 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 0000000000..ab9635ee30
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -0,0 +1,425 @@
+# 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
+import os
+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):
+ name = []
+ if kind.imported_from:
+ name.append(kind.imported_from["unique_name"])
+ if kind.parent_kind:
+ name.append(kind.parent_kind.name)
+ name.append(kind.name)
+ return ".".join(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.IsUnionKind(field.kind):
+ return "null"
+ if mojom.IsArrayKind(field.kind):
+ return "null"
+ if mojom.IsMapKind(field.kind):
+ return "null"
+ if mojom.IsInterfaceKind(field.kind):
+ return "new %sPtr()" % JavaScriptType(field.kind)
+ if mojom.IsInterfaceRequestKind(field.kind):
+ return "new bindings.InterfaceRequest()"
+ if mojom.IsAssociatedKind(field.kind):
+ return "null"
+ if mojom.IsEnumKind(field.kind):
+ return "0"
+ raise Exception("No valid default: %s" % field)
+
+
+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.IsUnionKind(kind):
+ return JavaScriptType(kind)
+ if mojom.IsArrayKind(kind):
+ array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
+ array_length = "" if kind.length is None else ", %d" % kind.length
+ element_type = ElementCodecType(kind.kind)
+ return "new codec.%s(%s%s)" % (array_type, element_type, array_length)
+ if mojom.IsInterfaceKind(kind):
+ return "new codec.%s(%sPtr)" % (
+ "NullableInterface" if mojom.IsNullableKind(kind) else "Interface",
+ JavaScriptType(kind))
+ if mojom.IsInterfaceRequestKind(kind):
+ return "codec.%s" % (
+ "NullableInterfaceRequest" if mojom.IsNullableKind(kind)
+ else "InterfaceRequest")
+ if mojom.IsAssociatedInterfaceKind(kind):
+ return "codec.AssociatedInterfaceNotSupported"
+ if mojom.IsAssociatedInterfaceRequestKind(kind):
+ return "codec.AssociatedInterfaceRequestNotSupported"
+ if mojom.IsEnumKind(kind):
+ return "new codec.Enum(%s)" % JavaScriptType(kind)
+ if mojom.IsMapKind(kind):
+ map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf"
+ key_type = ElementCodecType(kind.key_kind)
+ value_type = ElementCodecType(kind.value_kind)
+ return "new codec.%s(%s, %s)" % (map_type, key_type, value_type)
+ raise Exception("No codec type for %s" % kind)
+
+
+def ElementCodecType(kind):
+ return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind)
+
+
+def JavaScriptDecodeSnippet(kind):
+ if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
+ mojom.IsAnyInterfaceKind(kind)):
+ return "decodeStruct(%s)" % CodecType(kind)
+ if mojom.IsStructKind(kind):
+ return "decodeStructPointer(%s)" % JavaScriptType(kind)
+ if mojom.IsMapKind(kind):
+ return "decodeMapPointer(%s, %s)" % \
+ (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
+ if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+ return "decodeArrayPointer(codec.PackedBool)"
+ if mojom.IsArrayKind(kind):
+ return "decodeArrayPointer(%s)" % CodecType(kind.kind)
+ if mojom.IsUnionKind(kind):
+ return "decodeUnion(%s)" % CodecType(kind)
+ if mojom.IsEnumKind(kind):
+ return JavaScriptDecodeSnippet(mojom.INT32)
+ raise Exception("No decode snippet for %s" % kind)
+
+
+def JavaScriptEncodeSnippet(kind):
+ if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
+ mojom.IsAnyInterfaceKind(kind)):
+ return "encodeStruct(%s, " % CodecType(kind)
+ if mojom.IsUnionKind(kind):
+ return "encodeStruct(%s, " % JavaScriptType(kind)
+ if mojom.IsStructKind(kind):
+ return "encodeStructPointer(%s, " % JavaScriptType(kind)
+ if mojom.IsMapKind(kind):
+ return "encodeMapPointer(%s, %s, " % \
+ (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
+ if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
+ return "encodeArrayPointer(codec.PackedBool, ";
+ if mojom.IsArrayKind(kind):
+ return "encodeArrayPointer(%s, " % CodecType(kind.kind)
+ if mojom.IsEnumKind(kind):
+ return JavaScriptEncodeSnippet(mojom.INT32)
+ raise Exception("No encode snippet for %s" % kind)
+
+
+def JavaScriptUnionDecodeSnippet(kind):
+ if mojom.IsUnionKind(kind):
+ return "decodeStructPointer(%s)" % JavaScriptType(kind)
+ return JavaScriptDecodeSnippet(kind)
+
+
+def JavaScriptUnionEncodeSnippet(kind):
+ if mojom.IsUnionKind(kind):
+ return "encodeStructPointer(%s, " % JavaScriptType(kind)
+ return JavaScriptEncodeSnippet(kind)
+
+
+def JavaScriptFieldOffset(packed_field):
+ return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
+
+
+def JavaScriptNullableParam(field):
+ return "true" if mojom.IsNullableKind(field.kind) else "false"
+
+
+def GetArrayExpectedDimensionSizes(kind):
+ expected_dimension_sizes = []
+ while mojom.IsArrayKind(kind):
+ expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0)
+ kind = kind.kind
+ # Strings are serialized as variable-length arrays.
+ if (mojom.IsStringKind(kind)):
+ expected_dimension_sizes.append(0)
+ return expected_dimension_sizes
+
+
+def JavaScriptValidateArrayParams(field):
+ nullable = JavaScriptNullableParam(field)
+ element_kind = field.kind.kind
+ element_size = pack.PackedField.GetSizeForKind(element_kind)
+ expected_dimension_sizes = GetArrayExpectedDimensionSizes(
+ field.kind)
+ element_type = ElementCodecType(element_kind)
+ return "%s, %s, %s, %s, 0" % \
+ (element_size, element_type, nullable,
+ expected_dimension_sizes)
+
+
+def JavaScriptValidateEnumParams(field):
+ return JavaScriptType(field.kind)
+
+def JavaScriptValidateStructParams(field):
+ nullable = JavaScriptNullableParam(field)
+ struct_type = JavaScriptType(field.kind)
+ return "%s, %s" % (struct_type, nullable)
+
+def JavaScriptValidateUnionParams(field):
+ nullable = JavaScriptNullableParam(field)
+ union_type = JavaScriptType(field.kind)
+ return "%s, %s" % (union_type, nullable)
+
+def JavaScriptValidateMapParams(field):
+ nullable = JavaScriptNullableParam(field)
+ keys_type = ElementCodecType(field.kind.key_kind)
+ values_kind = field.kind.value_kind;
+ values_type = ElementCodecType(values_kind)
+ values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false"
+ return "%s, %s, %s, %s" % \
+ (nullable, keys_type, values_type, values_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.IsArrayKind(field.kind)
+
+def IsEnumField(field):
+ return mojom.IsEnumKind(field.kind)
+
+def IsStringPointerField(field):
+ return mojom.IsStringKind(field.kind)
+
+def IsStructPointerField(field):
+ return mojom.IsStructKind(field.kind)
+
+def IsMapPointerField(field):
+ return mojom.IsMapKind(field.kind)
+
+def IsHandleField(field):
+ return mojom.IsAnyHandleKind(field.kind)
+
+def IsInterfaceField(field):
+ return mojom.IsInterfaceKind(field.kind)
+
+def IsInterfaceRequestField(field):
+ return mojom.IsInterfaceRequestKind(field.kind)
+
+def IsUnionField(field):
+ return mojom.IsUnionKind(field.kind)
+
+def IsBoolField(field):
+ return mojom.IsBoolKind(field.kind)
+
+def IsObjectField(field):
+ return mojom.IsObjectKind(field.kind)
+
+def IsAnyHandleOrInterfaceField(field):
+ return mojom.IsAnyHandleOrInterfaceKind(field.kind)
+
+def IsEnumField(field):
+ return mojom.IsEnumKind(field.kind)
+
+def GetRelativePath(module, base_module):
+ return os.path.relpath(module.path, os.path.dirname(base_module.path))
+
+
+class Generator(generator.Generator):
+
+ js_filters = {
+ "decode_snippet": JavaScriptDecodeSnippet,
+ "default_value": JavaScriptDefaultValue,
+ "encode_snippet": JavaScriptEncodeSnippet,
+ "expression_to_text": ExpressionToText,
+ "field_offset": JavaScriptFieldOffset,
+ "has_callbacks": mojom.HasCallbacks,
+ "is_any_handle_or_interface_field": IsAnyHandleOrInterfaceField,
+ "is_array_pointer_field": IsArrayPointerField,
+ "is_bool_field": IsBoolField,
+ "is_enum_field": IsEnumField,
+ "is_handle_field": IsHandleField,
+ "is_interface_field": IsInterfaceField,
+ "is_interface_request_field": IsInterfaceRequestField,
+ "is_map_pointer_field": IsMapPointerField,
+ "is_object_field": IsObjectField,
+ "is_string_pointer_field": IsStringPointerField,
+ "is_struct_pointer_field": IsStructPointerField,
+ "is_union_field": IsUnionField,
+ "js_type": JavaScriptType,
+ "payload_size": JavaScriptPayloadSize,
+ "get_relative_path": GetRelativePath,
+ "stylize_method": generator.StudlyCapsToCamel,
+ "union_decode_snippet": JavaScriptUnionDecodeSnippet,
+ "union_encode_snippet": JavaScriptUnionEncodeSnippet,
+ "validate_array_params": JavaScriptValidateArrayParams,
+ "validate_enum_params": JavaScriptValidateEnumParams,
+ "validate_map_params": JavaScriptValidateMapParams,
+ "validate_nullable_params": JavaScriptNullableParam,
+ "validate_struct_params": JavaScriptValidateStructParams,
+ "validate_union_params": JavaScriptValidateUnionParams,
+ }
+
+ def GetParameters(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(),
+ "unions": self.GetUnions(),
+ "use_new_js_bindings": self.use_new_js_bindings,
+ "interfaces": self.GetInterfaces(),
+ "imported_interfaces": self.GetImportedInterfaces(),
+ }
+
+ @staticmethod
+ def GetTemplatePrefix():
+ return "js_templates"
+
+ @classmethod
+ def GetFilters(cls):
+ return cls.js_filters
+
+ @UseJinja("module.amd.tmpl")
+ def GenerateAMDModule(self):
+ return self.GetParameters()
+
+ def GenerateFiles(self, args):
+ if self.variant:
+ raise Exception("Variants not supported in JavaScript bindings.")
+
+ self.Write(self.GenerateAMDModule(),
+ self.MatchMojomFilePath("%s.js" % self.module.name))
+
+ def GetImports(self):
+ used_names = set()
+ for each_import in self.module.imports:
+ simple_name = each_import["module_name"].split(".")[0]
+
+ # Since each import is assigned a variable in JS, they need to have unique
+ # names.
+ unique_name = simple_name
+ counter = 0
+ while unique_name in used_names:
+ counter += 1
+ unique_name = simple_name + str(counter)
+
+ used_names.add(unique_name)
+ each_import["unique_name"] = unique_name + "$"
+ counter += 1
+ return self.module.imports
+
+ def GetImportedInterfaces(self):
+ interface_to_import = {};
+ for each_import in self.module.imports:
+ for each_interface in each_import["module"].interfaces:
+ name = each_interface.name
+ interface_to_import[name] = each_import["unique_name"] + "." + name
+ return interface_to_import;
+
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
new file mode 100644
index 0000000000..4a244fb5b1
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -0,0 +1,661 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+ # Indicates whether typemapping should be supported in this build
+ # configuration. This may be disabled when building external projects which
+ # depend on //mojo but which do not need/want all of the Chromium tree
+ # dependencies that come with typemapping.
+ #
+ # Note that (perhaps obviously) a huge amount of Chromium code will not build
+ # with typemapping disabled, so it is never valid to set this to |false| in
+ # any Chromium build configuration.
+ enable_mojom_typemapping = true
+}
+
+mojom_generator_root = "//mojo/public/tools/bindings"
+mojom_generator_script = "$mojom_generator_root/mojom_bindings_generator.py"
+mojom_generator_sources = [
+ "$mojom_generator_root/generators/mojom_cpp_generator.py",
+ "$mojom_generator_root/generators/mojom_js_generator.py",
+ "$mojom_generator_root/generators/mojom_java_generator.py",
+ "$mojom_generator_root/pylib/mojom/__init__.py",
+ "$mojom_generator_root/pylib/mojom/error.py",
+ "$mojom_generator_root/pylib/mojom/generate/__init__.py",
+ "$mojom_generator_root/pylib/mojom/generate/constant_resolver.py",
+ "$mojom_generator_root/pylib/mojom/generate/generator.py",
+ "$mojom_generator_root/pylib/mojom/generate/module.py",
+ "$mojom_generator_root/pylib/mojom/generate/pack.py",
+ "$mojom_generator_root/pylib/mojom/generate/template_expander.py",
+ "$mojom_generator_root/pylib/mojom/generate/translate.py",
+ "$mojom_generator_root/pylib/mojom/parse/__init__.py",
+ "$mojom_generator_root/pylib/mojom/parse/ast.py",
+ "$mojom_generator_root/pylib/mojom/parse/lexer.py",
+ "$mojom_generator_root/pylib/mojom/parse/parser.py",
+ "$mojom_generator_script",
+]
+
+if (enable_mojom_typemapping) {
+ if (!is_ios) {
+ _bindings_configuration_files = [
+ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni",
+ "//mojo/public/tools/bindings/blink_bindings_configuration.gni",
+ ]
+ } else {
+ _bindings_configuration_files =
+ [ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni" ]
+ }
+ _bindings_configurations = []
+ foreach(config_file, _bindings_configuration_files) {
+ _bindings_configurations += [ read_file(config_file, "scope") ]
+ }
+ foreach(configuration, _bindings_configurations) {
+ # Check that the mojom field of each typemap refers to a mojom that exists.
+ foreach(typemap, configuration.typemaps) {
+ _typemap_config = {
+ }
+ _typemap_config = typemap.config
+ read_file(_typemap_config.mojom, "")
+ }
+ if (is_mac && defined(configuration.typemaps_mac)) {
+ foreach(typemap, configuration.typemaps_mac) {
+ _typemap_config = {
+ }
+ _typemap_config = typemap.config
+ read_file(_typemap_config.mojom, "")
+ }
+ }
+ }
+} else {
+ _bindings_configuration_files = []
+ _bindings_configurations = [
+ {
+ typemaps = []
+ },
+ {
+ variant = "blink"
+ for_blink = true
+ typemaps = []
+ },
+ ]
+}
+
+# Generates targets for building C++, JavaScript and Java bindings from mojom
+# files. The output files will go under the generated file directory tree with
+# the same path as each input file.
+#
+# Other targets should depend on one of these generated targets (where "foo"
+# is the target name):
+#
+# foo
+# C++ and Javascript bindings. Other mojom targets should also depend on
+# this target.
+#
+# foo_blink
+# C++ bindings using Blink standard types.
+#
+# foo_java
+# Java bindings.
+#
+# Parameters:
+#
+# sources (optional if one of the deps sets listed below is present)
+# List of source .mojom files to compile.
+#
+# deps (optional)
+# Note: this can contain only other mojom targets.
+#
+# DEPRECATED: This is synonymous with public_deps because all mojom
+# dependencies must be public by design. Please use public_deps.
+#
+# public_deps (optional)
+# Note: this can contain only other mojom targets.
+#
+# import_dirs (optional)
+# List of import directories that will get added when processing sources.
+#
+# testonly (optional)
+#
+# visibility (optional)
+#
+# visibility_blink (optional)
+# The value to use for visibility for the blink variant. If unset,
+# |visibility| is used.
+#
+# use_once_callback (optional)
+# If set to true, generated classes will use base::OnceCallback instead of
+# base::RepeatingCallback.
+# Default value is false.
+# TODO(dcheng):
+# - Convert everything to use OnceCallback.
+# - Remove support for the old mode.
+#
+# cpp_only (optional)
+# If set to true, only the C++ bindings targets will be generated.
+#
+# use_new_js_bindings (optional)
+# If set to true, the generated JS code will use the new module loading
+# approach and the core API exposed by Web IDL.
+#
+# TODO(yzshen): Switch all existing users to use_new_js_bindings=true and
+# remove the old mode.
+#
+# The following parameters are used to support the component build. They are
+# needed so that bindings which are linked with a component can use the same
+# export settings for classes. The first three are for the chromium variant, and
+# the last three are for the blink variant.
+# export_class_attribute (optional)
+# The attribute to add to the class declaration. e.g. "CONTENT_EXPORT"
+# export_define (optional)
+# A define to be added to the source_set which is needed by the export
+# header. e.g. "CONTENT_IMPLEMENTATION=1"
+# export_header (optional)
+# A header to be added to the generated bindings to support the component
+# build. e.g. "content/common/content_export.h"
+# export_class_attribute_blink (optional)
+# export_define_blink (optional)
+# export_header_blink (optional)
+# These three parameters are the blink variants of the previous 3.
+#
+# The following parameters are used to correct component build dependencies.
+# They are needed so mojom-mojom dependencies follow the rule that dependencies
+# on a source set in another component are replaced by a dependency on the
+# containing component. The first two are for the chromium variant; the other
+# two are for the blink variant.
+# overridden_deps (optional)
+# The list of mojom deps to be overridden.
+# component_deps (optional)
+# The list of component deps to add to replace overridden_deps.
+# overridden_deps_blink (optional)
+# component_deps_blink (optional)
+# These two parameters are the blink variants of the previous two.
+template("mojom") {
+ assert(
+ defined(invoker.sources) || defined(invoker.deps) ||
+ defined(invoker.public_deps),
+ "\"sources\" or \"deps\" must be defined for the $target_name template.")
+ if (defined(invoker.export_class_attribute) ||
+ defined(invoker.export_define) || defined(invoker.export_header)) {
+ assert(defined(invoker.export_class_attribute))
+ assert(defined(invoker.export_define))
+ assert(defined(invoker.export_header))
+ }
+ if (defined(invoker.export_class_attribute_blink) ||
+ defined(invoker.export_define_blink) ||
+ defined(invoker.export_header_blink)) {
+ assert(defined(invoker.export_class_attribute_blink))
+ assert(defined(invoker.export_define_blink))
+ assert(defined(invoker.export_header_blink))
+ }
+ if (defined(invoker.overridden_deps) || defined(invoker.component_deps)) {
+ assert(defined(invoker.overridden_deps))
+ assert(defined(invoker.component_deps))
+ }
+
+ if (defined(invoker.overridden_deps_blink) ||
+ defined(invoker.component_deps_blink)) {
+ assert(defined(invoker.overridden_deps_blink))
+ assert(defined(invoker.component_deps_blink))
+ }
+
+ all_deps = []
+ if (defined(invoker.deps)) {
+ all_deps += invoker.deps
+ }
+ if (defined(invoker.public_deps)) {
+ all_deps += invoker.public_deps
+ }
+
+ group("${target_name}__is_mojom") {
+ }
+
+ # Explicitly ensure that all dependencies (invoker.deps and
+ # invoker.public_deps) are mojom targets.
+ group("${target_name}__check_deps_are_all_mojom") {
+ deps = []
+ foreach(d, all_deps) {
+ name = get_label_info(d, "label_no_toolchain")
+ toolchain = get_label_info(d, "toolchain")
+ deps += [ "${name}__is_mojom(${toolchain})" ]
+ }
+ }
+
+ # Generate code that is shared by different variants.
+ if (defined(invoker.sources)) {
+ common_generator_args = [
+ "--use_bundled_pylibs",
+ "generate",
+ "{{source}}",
+ "-d",
+ rebase_path("//", root_build_dir),
+ "-I",
+ rebase_path("//", root_build_dir),
+ "-o",
+ rebase_path(root_gen_dir),
+ "--bytecode_path",
+ rebase_path("$root_gen_dir/mojo/public/tools/bindings"),
+ ]
+
+ if (defined(invoker.import_dirs)) {
+ foreach(import_dir, invoker.import_dirs) {
+ common_generator_args += [
+ "-I",
+ rebase_path(import_dir, root_build_dir),
+ ]
+ }
+ }
+
+ generator_shared_cpp_outputs = [
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.cc",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.h",
+ ]
+ generator_shared_target_name = "${target_name}_shared__generator"
+ action_foreach(generator_shared_target_name) {
+ script = mojom_generator_script
+ inputs = mojom_generator_sources
+ sources = invoker.sources
+ deps = [
+ "//mojo/public/tools/bindings:precompile_templates",
+ ]
+ outputs = generator_shared_cpp_outputs
+ args = common_generator_args
+ args += [
+ "--generate_non_variant_code",
+ "-g",
+ "c++",
+ ]
+ depfile = "{{source_gen_dir}}/${generator_shared_target_name}_{{source_name_part}}.d"
+ args += [
+ "--depfile",
+ depfile,
+ "--depfile_target",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
+ ]
+ }
+ }
+
+ shared_cpp_sources_suffix = "shared_cpp_sources"
+ shared_cpp_sources_target_name = "${target_name}_${shared_cpp_sources_suffix}"
+ source_set(shared_cpp_sources_target_name) {
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ deps = []
+ if (defined(invoker.sources)) {
+ sources =
+ process_file_template(invoker.sources, generator_shared_cpp_outputs)
+ deps += [ ":$generator_shared_target_name" ]
+ }
+ public_deps = []
+ foreach(d, all_deps) {
+ # Resolve the name, so that a target //mojo/something becomes
+ # //mojo/something:something and we can append shared_cpp_sources_suffix
+ # to get the cpp dependency name.
+ full_name = get_label_info("$d", "label_no_toolchain")
+ public_deps += [ "${full_name}_${shared_cpp_sources_suffix}" ]
+ }
+ }
+
+ # Generate code for variants.
+ foreach(bindings_configuration, _bindings_configurations) {
+ cpp_only = false
+ if (defined(invoker.cpp_only)) {
+ cpp_only = invoker.cpp_only
+ }
+ variant_suffix = ""
+ if (defined(bindings_configuration.variant)) {
+ variant = bindings_configuration.variant
+ variant_suffix = "_${variant}"
+ cpp_only = true
+ }
+ type_mappings_target_name = "${target_name}${variant_suffix}__type_mappings"
+ type_mappings_path =
+ "$target_gen_dir/${target_name}${variant_suffix}__type_mappings"
+ active_typemaps = []
+ enabled_sources = []
+ if (defined(invoker.sources)) {
+ generator_cpp_outputs = []
+ generator_js_outputs = []
+ generator_java_outputs = []
+ variant_dash_suffix = ""
+ if (defined(variant)) {
+ variant_dash_suffix = "-${variant}"
+ }
+ generator_cpp_outputs += [
+ "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.cc",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.h",
+ ]
+ enabled_sources = []
+ if (defined(bindings_configuration.blacklist)) {
+ foreach(source, invoker.sources) {
+ blacklisted = false
+ foreach(blacklisted_source, bindings_configuration.blacklist) {
+ if (get_path_info(source, "abspath") == blacklisted_source) {
+ blacklisted = true
+ }
+ }
+ if (!blacklisted) {
+ enabled_sources += [ source ]
+ }
+ }
+ } else {
+ enabled_sources = invoker.sources
+ }
+ foreach(source, enabled_sources) {
+ # TODO(sammc): Use a map instead of a linear scan when GN supports maps.
+ foreach(typemap, bindings_configuration.typemaps) {
+ _typemap_config = {
+ }
+ _typemap_config = typemap.config
+ if (get_path_info(source, "abspath") == _typemap_config.mojom) {
+ active_typemaps += [ typemap ]
+ }
+ }
+ if (is_mac && defined(bindings_configuration.typemaps_mac)) {
+ foreach(typemap, bindings_configuration.typemaps_mac) {
+ _typemap_config = {
+ }
+ _typemap_config = typemap.config
+ if (get_path_info(source, "abspath") == _typemap_config.mojom) {
+ active_typemaps += [ typemap ]
+ }
+ }
+ }
+ }
+
+ if (!cpp_only) {
+ generator_js_outputs =
+ [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js" ]
+ generator_java_outputs =
+ [ "{{source_gen_dir}}/{{source_name_part}}.mojom.srcjar" ]
+ }
+ generator_target_name = "${target_name}${variant_suffix}__generator"
+ action_foreach(generator_target_name) {
+ script = mojom_generator_script
+ inputs = mojom_generator_sources
+ sources = invoker.sources
+ deps = [
+ ":$type_mappings_target_name",
+ "//mojo/public/tools/bindings:precompile_templates",
+ ]
+ outputs = generator_cpp_outputs + generator_java_outputs +
+ generator_js_outputs
+ args = common_generator_args
+
+ if (cpp_only) {
+ args += [
+ "-g",
+ "c++",
+ ]
+ } else {
+ args += [
+ "-g",
+ "c++,javascript,java",
+ ]
+ }
+
+ if (defined(bindings_configuration.variant)) {
+ args += [
+ "--variant",
+ bindings_configuration.variant,
+ ]
+ }
+ depfile =
+ "{{source_gen_dir}}/${generator_target_name}_{{source_name_part}}.d"
+ args += [
+ "--depfile",
+ depfile,
+ "--depfile_target",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.cc",
+ ]
+
+ args += [
+ "--typemap",
+ rebase_path(type_mappings_path, root_build_dir),
+ ]
+
+ if (defined(bindings_configuration.for_blink) &&
+ bindings_configuration.for_blink) {
+ args += [ "--for_blink" ]
+ if (defined(invoker.export_class_attribute_blink)) {
+ args += [
+ "--export_attribute",
+ invoker.export_class_attribute_blink,
+ "--export_header",
+ invoker.export_header_blink,
+ ]
+ }
+ } else {
+ if (defined(invoker.export_class_attribute)) {
+ args += [
+ "--export_attribute",
+ invoker.export_class_attribute,
+ "--export_header",
+ invoker.export_header,
+ ]
+ }
+ }
+
+ if (defined(invoker.use_once_callback) && invoker.use_once_callback) {
+ args += [ "--use_once_callback" ]
+ }
+
+ if (defined(invoker.use_new_js_bindings) &&
+ invoker.use_new_js_bindings) {
+ args += [ "--use_new_js_bindings" ]
+ }
+ }
+ }
+
+ action(type_mappings_target_name) {
+ inputs = _bindings_configuration_files
+ outputs = [
+ type_mappings_path,
+ ]
+ script = "$mojom_generator_root/generate_type_mappings.py"
+ deps = []
+ args = [
+ "--output",
+ rebase_path(type_mappings_path, root_build_dir),
+ ]
+
+ foreach(d, all_deps) {
+ name = get_label_info(d, "label_no_toolchain")
+ toolchain = get_label_info(d, "toolchain")
+ dependency_output = "${name}${variant_suffix}__type_mappings"
+ dependency_target = "${dependency_output}(${toolchain})"
+ deps += [ dependency_target ]
+ dependency_output_dir =
+ get_label_info(dependency_output, "target_gen_dir")
+ dependency_name = get_label_info(dependency_output, "name")
+ dependency_path =
+ rebase_path("$dependency_output_dir/${dependency_name}",
+ root_build_dir)
+ args += [
+ "--dependency",
+ dependency_path,
+ ]
+ }
+
+ if (enabled_sources != []) {
+ # TODO(sammc): Pass the typemap description in a file to avoid command
+ # line length limitations.
+ typemap_description = []
+ foreach(typemap, active_typemaps) {
+ _typemap_config = {
+ }
+ _typemap_config = typemap.config
+ typemap_description += [ "--start-typemap" ]
+ if (defined(_typemap_config.public_headers)) {
+ foreach(value, _typemap_config.public_headers) {
+ typemap_description += [ "public_headers=$value" ]
+ }
+ }
+ if (defined(_typemap_config.traits_headers)) {
+ foreach(value, _typemap_config.traits_headers) {
+ typemap_description += [ "traits_headers=$value" ]
+ }
+ }
+ foreach(value, _typemap_config.type_mappings) {
+ typemap_description += [ "type_mappings=$value" ]
+ }
+
+ # The typemap configuration files are not actually used as inputs here
+ # but this establishes a necessary build dependency to ensure that
+ # typemap changes force a rebuild of affected targets.
+ inputs += [ typemap.filename ]
+ }
+ args += typemap_description
+ }
+ }
+
+ source_set("${target_name}${variant_suffix}") {
+ if (defined(bindings_configuration.for_blink) &&
+ bindings_configuration.for_blink &&
+ defined(invoker.visibility_blink)) {
+ visibility = invoker.visibility_blink
+ } else if (defined(invoker.visibility)) {
+ visibility = invoker.visibility
+ }
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ if (defined(invoker.sources) && !defined(bindings_configuration.variant)) {
+ data = process_file_template(enabled_sources, generator_js_outputs)
+ }
+ defines = []
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ if (defined(invoker.export_define)) {
+ defines += [ invoker.export_define ]
+ }
+ if (defined(invoker.export_define_blink)) {
+ defines += [ invoker.export_define_blink ]
+ }
+ if (enabled_sources != []) {
+ sources = process_file_template(enabled_sources, generator_cpp_outputs)
+ }
+ deps = [
+ "//mojo/public/cpp/bindings:struct_traits",
+ "//mojo/public/interfaces/bindings:bindings__generator",
+ "//mojo/public/interfaces/bindings:bindings_shared__generator",
+ ]
+ public_deps = [
+ ":$shared_cpp_sources_target_name",
+ "//base",
+ "//mojo/public/cpp/bindings",
+ ]
+ if (enabled_sources != []) {
+ public_deps += [ ":$generator_target_name" ]
+ }
+ foreach(d, all_deps) {
+ # Resolve the name, so that a target //mojo/something becomes
+ # //mojo/something:something and we can append variant_suffix to
+ # get the cpp dependency name.
+ full_name = get_label_info("$d", "label_no_toolchain")
+ public_deps += [ "${full_name}${variant_suffix}" ]
+ }
+ if (defined(bindings_configuration.for_blink) &&
+ bindings_configuration.for_blink) {
+ if (defined(invoker.overridden_deps_blink)) {
+ foreach(d, invoker.overridden_deps_blink) {
+ # Resolve the name, so that a target //mojo/something becomes
+ # //mojo/something:something and we can append variant_suffix
+ # to get the cpp dependency name.
+ full_name = get_label_info("$d", "label_no_toolchain")
+ public_deps -= [ "${full_name}${variant_suffix}" ]
+ }
+ public_deps += invoker.component_deps_blink
+ }
+ } else {
+ if (defined(invoker.overridden_deps)) {
+ foreach(d, invoker.overridden_deps) {
+ # Resolve the name, so that a target //mojo/something becomes
+ # //mojo/something:something and we can append variant_suffix
+ # to get the cpp dependency name.
+ full_name = get_label_info("$d", "label_no_toolchain")
+ public_deps -= [ "${full_name}${variant_suffix}" ]
+ }
+ public_deps += invoker.component_deps
+ }
+ }
+ foreach(typemap, active_typemaps) {
+ _typemap_config = {
+ }
+ _typemap_config = typemap.config
+ if (defined(_typemap_config.public_headers)) {
+ sources += _typemap_config.public_headers
+ }
+ if (defined(_typemap_config.traits_headers)) {
+ sources += _typemap_config.traits_headers
+ }
+ if (defined(_typemap_config.sources)) {
+ sources += _typemap_config.sources
+ }
+ if (defined(_typemap_config.public_deps)) {
+ public_deps += _typemap_config.public_deps
+ }
+ if (defined(_typemap_config.deps)) {
+ deps += _typemap_config.deps
+ }
+ }
+ if (defined(bindings_configuration.for_blink) &&
+ bindings_configuration.for_blink) {
+ public_deps += [ "//mojo/public/cpp/bindings:wtf_support" ]
+ }
+ }
+
+ if (!cpp_only && is_android) {
+ import("//build/config/android/rules.gni")
+
+ java_srcjar_target_name = target_name + "_java_sources"
+ action(java_srcjar_target_name) {
+ script = "//mojo/public/tools/gn/zip.py"
+ inputs = []
+ if (enabled_sources != []) {
+ inputs =
+ process_file_template(enabled_sources, generator_java_outputs)
+ }
+ output = "$target_gen_dir/$target_name.srcjar"
+ outputs = [
+ output,
+ ]
+ rebase_inputs = rebase_path(inputs, root_build_dir)
+ rebase_output = rebase_path(output, root_build_dir)
+ args = [
+ "--zip-inputs=$rebase_inputs",
+ "--output=$rebase_output",
+ ]
+ deps = []
+ if (enabled_sources != []) {
+ deps = [
+ ":$generator_target_name",
+ ]
+ }
+ }
+
+ java_target_name = target_name + "_java"
+ android_library(java_target_name) {
+ deps = [
+ "//base:base_java",
+ "//mojo/public/java:bindings_java",
+ "//mojo/public/java:system_java",
+ ]
+
+ foreach(d, all_deps) {
+ # Resolve the name, so that a target //mojo/something becomes
+ # //mojo/something:something and we can append "_java" to get the java
+ # dependency name.
+ full_name = get_label_info(d, "label_no_toolchain")
+ deps += [ "${full_name}_java" ]
+ }
+
+ srcjar_deps = [ ":$java_srcjar_target_name" ]
+ run_findbugs_override = false
+ }
+ }
+ }
+}
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 0000000000..a9650d7764
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -0,0 +1,336 @@
+#!/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 json
+import os
+import pprint
+import re
+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_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
+import mojom.fileutil as fileutil
+from mojom.generate import translate
+from mojom.generate import template_expander
+from mojom.parse.parser import Parse
+
+
+_BUILTIN_GENERATORS = {
+ "c++": "mojom_cpp_generator.py",
+ "javascript": "mojom_js_generator.py",
+ "java": "mojom_java_generator.py",
+}
+
+
+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(",")]:
+ language = generator_name.lower()
+ if language in _BUILTIN_GENERATORS:
+ generator_name = os.path.join(script_dir, "generators",
+ _BUILTIN_GENERATORS[language])
+ 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[language] = 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)]))
+
+
+class RelativePath(object):
+ """Represents a path relative to the source tree."""
+ def __init__(self, path, source_root):
+ self.path = path
+ self.source_root = source_root
+
+ def relative_path(self):
+ return os.path.relpath(os.path.abspath(self.path),
+ os.path.abspath(self.source_root))
+
+
+def FindImportFile(rel_dir, file_name, search_rel_dirs):
+ """Finds |file_name| in either |rel_dir| or |search_rel_dirs|. Returns a
+ RelativePath with first file found, or an arbitrary non-existent file
+ otherwise."""
+ for rel_search_dir in [rel_dir] + search_rel_dirs:
+ path = os.path.join(rel_search_dir.path, file_name)
+ if os.path.isfile(path):
+ return RelativePath(path, rel_search_dir.source_root)
+ return RelativePath(os.path.join(rel_dir.path, file_name),
+ rel_dir.source_root)
+
+
+class MojomProcessor(object):
+ """Parses mojom files and creates ASTs for them.
+
+ Attributes:
+ _processed_files: {Dict[str, mojom.generate.module.Module]} Mapping from
+ relative mojom filename paths to the module AST for that mojom file.
+ """
+ def __init__(self, should_generate):
+ self._should_generate = should_generate
+ self._processed_files = {}
+ self._parsed_files = {}
+ self._typemap = {}
+
+ def LoadTypemaps(self, typemaps):
+ # Support some very simple single-line comments in typemap JSON.
+ comment_expr = r"^\s*//.*$"
+ def no_comments(line):
+ return not re.match(comment_expr, line)
+ for filename in typemaps:
+ with open(filename) as f:
+ typemaps = json.loads("".join(filter(no_comments, f.readlines())))
+ for language, typemap in typemaps.iteritems():
+ language_map = self._typemap.get(language, {})
+ language_map.update(typemap)
+ self._typemap[language] = language_map
+
+ def ProcessFile(self, args, remaining_args, generator_modules, filename):
+ self._ParseFileAndImports(RelativePath(filename, args.depth),
+ args.import_directories, [])
+
+ return self._GenerateModule(args, remaining_args, generator_modules,
+ RelativePath(filename, args.depth))
+
+ def _GenerateModule(self, args, remaining_args, generator_modules,
+ rel_filename):
+ # Return the already-generated module.
+ if rel_filename.path in self._processed_files:
+ return self._processed_files[rel_filename.path]
+ tree = self._parsed_files[rel_filename.path]
+
+ dirname, name = os.path.split(rel_filename.path)
+
+ # Process all our imports first and collect the module object for each.
+ # We use these to generate proper type info.
+ imports = {}
+ for parsed_imp in tree.import_list:
+ rel_import_file = FindImportFile(
+ RelativePath(dirname, rel_filename.source_root),
+ parsed_imp.import_filename, args.import_directories)
+ imports[parsed_imp.import_filename] = self._GenerateModule(
+ args, remaining_args, generator_modules, rel_import_file)
+
+ module = translate.OrderedModule(tree, name, imports)
+
+ # Set the path as relative to the source root.
+ module.path = rel_filename.relative_path()
+
+ # Normalize to unix-style path here to keep the generators simpler.
+ module.path = module.path.replace('\\', '/')
+
+ if self._should_generate(rel_filename.path):
+ for language, generator_module in generator_modules.iteritems():
+ generator = generator_module.Generator(
+ module, args.output_dir, typemap=self._typemap.get(language, {}),
+ variant=args.variant, bytecode_path=args.bytecode_path,
+ for_blink=args.for_blink,
+ use_once_callback=args.use_once_callback,
+ use_new_js_bindings=args.use_new_js_bindings,
+ export_attribute=args.export_attribute,
+ export_header=args.export_header,
+ generate_non_variant_code=args.generate_non_variant_code)
+ 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.
+ self._processed_files[rel_filename.path] = module
+ return module
+
+ def _ParseFileAndImports(self, rel_filename, import_directories,
+ imported_filename_stack):
+ # Ignore already-parsed files.
+ if rel_filename.path in self._parsed_files:
+ return
+
+ if rel_filename.path in imported_filename_stack:
+ print "%s: Error: Circular dependency" % rel_filename.path + \
+ MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
+ sys.exit(1)
+
+ try:
+ with open(rel_filename.path) as f:
+ source = f.read()
+ except IOError as e:
+ print "%s: Error: %s" % (rel_filename.path, e.strerror) + \
+ MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
+ sys.exit(1)
+
+ try:
+ tree = Parse(source, rel_filename.path)
+ except Error as e:
+ full_stack = imported_filename_stack + [rel_filename.path]
+ print str(e) + MakeImportStackMessage(full_stack)
+ sys.exit(1)
+
+ dirname = os.path.split(rel_filename.path)[0]
+ for imp_entry in tree.import_list:
+ import_file_entry = FindImportFile(
+ RelativePath(dirname, rel_filename.source_root),
+ imp_entry.import_filename, import_directories)
+ self._ParseFileAndImports(import_file_entry, import_directories,
+ imported_filename_stack + [rel_filename.path])
+
+ self._parsed_files[rel_filename.path] = tree
+
+
+def _Generate(args, remaining_args):
+ if args.variant == "none":
+ args.variant = None
+
+ for idx, import_dir in enumerate(args.import_directories):
+ tokens = import_dir.split(":")
+ if len(tokens) >= 2:
+ args.import_directories[idx] = RelativePath(tokens[0], tokens[1])
+ else:
+ args.import_directories[idx] = RelativePath(tokens[0], args.depth)
+ generator_modules = LoadGenerators(args.generators_string)
+
+ fileutil.EnsureDirectoryExists(args.output_dir)
+
+ processor = MojomProcessor(lambda filename: filename in args.filename)
+ processor.LoadTypemaps(set(args.typemaps))
+ for filename in args.filename:
+ processor.ProcessFile(args, remaining_args, generator_modules, filename)
+ if args.depfile:
+ assert args.depfile_target
+ with open(args.depfile, 'w') as f:
+ f.write('%s: %s' % (
+ args.depfile_target,
+ ' '.join(processor._parsed_files.keys())))
+
+ return 0
+
+
+def _Precompile(args, _):
+ generator_modules = LoadGenerators(",".join(_BUILTIN_GENERATORS.keys()))
+
+ template_expander.PrecompileTemplates(generator_modules, args.output_dir)
+ return 0
+
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generate bindings from mojom files.")
+ parser.add_argument("--use_bundled_pylibs", action="store_true",
+ help="use Python modules bundled in the SDK")
+
+ subparsers = parser.add_subparsers()
+ generate_parser = subparsers.add_parser(
+ "generate", description="Generate bindings from mojom files.")
+ generate_parser.add_argument("filename", nargs="+",
+ help="mojom input file")
+ generate_parser.add_argument("-d", "--depth", dest="depth", default=".",
+ help="depth from source root")
+ generate_parser.add_argument("-o", "--output_dir", dest="output_dir",
+ default=".",
+ help="output directory for generated files")
+ generate_parser.add_argument("-g", "--generators",
+ dest="generators_string",
+ metavar="GENERATORS",
+ default="c++,javascript,java",
+ help="comma-separated list of generators")
+ generate_parser.add_argument(
+ "-I", dest="import_directories", action="append", metavar="directory",
+ default=[],
+ help="add a directory to be searched for import files. The depth from "
+ "source root can be specified for each import by appending it after "
+ "a colon")
+ generate_parser.add_argument("--typemap", action="append", metavar="TYPEMAP",
+ default=[], dest="typemaps",
+ help="apply TYPEMAP to generated output")
+ generate_parser.add_argument("--variant", dest="variant", default=None,
+ help="output a named variant of the bindings")
+ generate_parser.add_argument(
+ "--bytecode_path", type=str, required=True, help=(
+ "the path from which to load template bytecode; to generate template "
+ "bytecode, run %s precompile BYTECODE_PATH" % os.path.basename(
+ sys.argv[0])))
+ generate_parser.add_argument("--for_blink", action="store_true",
+ help="Use WTF types as generated types for mojo "
+ "string/array/map.")
+ generate_parser.add_argument(
+ "--use_once_callback", action="store_true",
+ help="Use base::OnceCallback instead of base::RepeatingCallback.")
+ generate_parser.add_argument(
+ "--use_new_js_bindings", action="store_true",
+ help="Use the new module loading approach and the core API exposed by "
+ "Web IDL. This option only affects the JavaScript bindings.")
+ generate_parser.add_argument(
+ "--export_attribute", type=str, default="",
+ help="Optional attribute to specify on class declaration to export it "
+ "for the component build.")
+ generate_parser.add_argument(
+ "--export_header", type=str, default="",
+ help="Optional header to include in the generated headers to support the "
+ "component build.")
+ generate_parser.add_argument(
+ "--generate_non_variant_code", action="store_true",
+ help="Generate code that is shared by different variants.")
+ generate_parser.add_argument(
+ "--depfile", type=str,
+ help="A file into which the list of input files will be written.")
+ generate_parser.add_argument(
+ "--depfile_target", type=str,
+ help="The target name to use in the depfile.")
+ generate_parser.set_defaults(func=_Generate)
+
+ precompile_parser = subparsers.add_parser("precompile",
+ description="Precompile templates for the mojom bindings generator.")
+ precompile_parser.add_argument(
+ "-o", "--output_dir", dest="output_dir", default=".",
+ help="output directory for precompiled templates")
+ precompile_parser.set_defaults(func=_Precompile)
+
+ args, remaining_args = parser.parse_known_args()
+ return args.func(args, remaining_args)
+
+
+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 0000000000..de388561cb
--- /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 0000000000..e69de29bb2
--- /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 0000000000..99522b9507
--- /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/fileutil.py b/mojo/public/tools/bindings/pylib/mojom/fileutil.py
new file mode 100644
index 0000000000..b321e9f543
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/fileutil.py
@@ -0,0 +1,18 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import errno
+import os.path
+
+def EnsureDirectoryExists(path, always_try_to_create=False):
+ """A wrapper for os.makedirs that does not error if the directory already
+ exists. A different process could be racing to create this directory."""
+
+ if not os.path.exists(path) or always_try_to_create:
+ try:
+ os.makedirs(path)
+ except OSError as e:
+ # There may have been a race to create this directory.
+ if e.errno != errno.EEXIST:
+ raise
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 0000000000..e69de29bb2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py b/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py
new file mode 100644
index 0000000000..c8b21f2629
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/constant_resolver.py
@@ -0,0 +1,91 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Resolves the values used for constants and enums."""
+
+from itertools import ifilter
+import mojom.generate.module as mojom
+
+def ResolveConstants(module, expression_to_text):
+ in_progress = set()
+ computed = set()
+
+ def GetResolvedValue(named_value):
+ assert isinstance(named_value, (mojom.EnumValue, mojom.ConstantValue))
+ 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.resolved_value
+ else:
+ ResolveConstant(named_value.constant)
+ named_value.resolved_value = named_value.constant.resolved_value
+ return named_value.resolved_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)):
+ resolved_value = GetResolvedValue(constant.value)
+ else:
+ resolved_value = expression_to_text(constant.value)
+ constant.resolved_value = resolved_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):
+ resolved_value = GetResolvedValue(field.value)
+ elif isinstance(field.value, str):
+ resolved_value = int(field.value, 0)
+ else:
+ raise RuntimeError('Unexpected value: %s' % field.value)
+ else:
+ resolved_value = default_value
+ field.resolved_value = resolved_value
+ in_progress.remove(field)
+ computed.add(field)
+
+ current_value = 0
+ for field in enum.fields:
+ ResolveEnumField(enum, field, current_value)
+ current_value = field.resolved_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.resolved_value = GetResolvedValue(field.default)
+
+ for interface in module.interfaces:
+ for constant in interface.constants:
+ ResolveConstant(constant)
+ for enum in interface.enums:
+ ResolveEnum(enum)
+
+ return module
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 0000000000..0e64af78a1
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -0,0 +1,153 @@
+# 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 mojom.fileutil as fileutil
+import pack
+
+def ExpectedArraySize(kind):
+ if mojom.IsArrayKind(kind):
+ return kind.length
+ return None
+
+def StudlyCapsToCamel(studly):
+ return studly[0].lower() + studly[1:]
+
+def UnderToCamel(under):
+ """Converts underscore_separated strings to CamelCase strings."""
+ return ''.join(word.capitalize() for word in under.split('_'))
+
+def WriteFile(contents, full_path):
+ # Make sure the containing directory exists.
+ full_dir = os.path.dirname(full_path)
+ fileutil.EnsureDirectoryExists(full_dir)
+
+ # Dump the data to disk.
+ with open(full_path, "w+") as f:
+ f.write(contents)
+
+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, typemap=None, variant=None,
+ bytecode_path=None, for_blink=False, use_once_callback=False,
+ use_new_js_bindings=False, export_attribute=None,
+ export_header=None, generate_non_variant_code=False):
+ self.module = module
+ self.output_dir = output_dir
+ self.typemap = typemap or {}
+ self.variant = variant
+ self.bytecode_path = bytecode_path
+ self.for_blink = for_blink
+ self.use_once_callback = use_once_callback
+ self.use_new_js_bindings = use_new_js_bindings
+ self.export_attribute = export_attribute
+ self.export_header = export_header
+ self.generate_non_variant_code = generate_non_variant_code
+
+ def GetStructsFromMethods(self):
+ result = []
+ for interface in self.module.interfaces:
+ for method in interface.methods:
+ result.append(self._GetStructFromMethod(method))
+ if method.response_parameters != None:
+ result.append(self._GetResponseStructFromMethod(method))
+ return result
+
+ def GetStructs(self):
+ return map(partial(self._AddStructComputedData, True), self.module.structs)
+
+ def GetUnions(self):
+ return map(self._AddUnionComputedData, self.module.unions)
+
+ def GetInterfaces(self):
+ return map(self._AddInterfaceComputedData, self.module.interfaces)
+
+ # Prepend the filename with a directory that matches the directory of the
+ # original .mojom file, relative to the import root.
+ def MatchMojomFilePath(self, filename):
+ return os.path.join(os.path.dirname(self.module.path), filename)
+
+ def Write(self, contents, filename):
+ if self.output_dir is None:
+ print contents
+ return
+ full_path = os.path.join(self.output_dir, filename)
+ WriteFile(contents, full_path)
+
+ 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 {}
+
+ def _AddStructComputedData(self, exported, struct):
+ """Adds computed data to the given struct. The data is computed once and
+ used repeatedly in the generation process."""
+ struct.packed = pack.PackedStruct(struct)
+ struct.bytes = pack.GetByteLayout(struct.packed)
+ struct.versions = pack.GetVersionInfo(struct.packed)
+ struct.exported = exported
+ return struct
+
+ def _AddUnionComputedData(self, union):
+ """Adds computed data to the given union. The data is computed once and
+ used repeatedly in the generation process."""
+ ordinal = 0
+ for field in union.fields:
+ if field.ordinal is not None:
+ ordinal = field.ordinal
+ field.ordinal = ordinal
+ ordinal += 1
+ return union
+
+ def _AddInterfaceComputedData(self, interface):
+ """Adds computed data to the given interface. The data is computed once and
+ used repeatedly in the generation process."""
+ interface.version = 0
+ for method in interface.methods:
+ if method.min_version is not None:
+ interface.version = max(interface.version, method.min_version)
+
+ method.param_struct = self._GetStructFromMethod(method)
+ interface.version = max(interface.version,
+ method.param_struct.versions[-1].version)
+
+ if method.response_parameters is not None:
+ method.response_param_struct = self._GetResponseStructFromMethod(method)
+ interface.version = max(
+ interface.version,
+ method.response_param_struct.versions[-1].version)
+ else:
+ method.response_param_struct = None
+ return interface
+
+ def _GetStructFromMethod(self, 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,
+ attributes=param.attributes)
+ return self._AddStructComputedData(False, struct)
+
+ def _GetResponseStructFromMethod(self, 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,
+ attributes=param.attributes)
+ return self._AddStructComputedData(False, struct)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py
new file mode 100644
index 0000000000..9966b0b7f8
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py
@@ -0,0 +1,24 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import module as mojom
+import generator
+
+class TestGenerator(unittest.TestCase):
+
+ def testGetUnionsAddsOrdinals(self):
+ module = mojom.Module()
+ union = module.AddUnion('a')
+ union.AddField('a', mojom.BOOL)
+ union.AddField('b', mojom.BOOL)
+ union.AddField('c', mojom.BOOL, ordinal=10)
+ union.AddField('d', mojom.BOOL)
+
+ gen = generator.Generator(module)
+ union = gen.GetUnions()[0]
+ ordinals = [field.ordinal for field in union.fields]
+
+ self.assertEquals([0, 1, 10, 11], ordinals)
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 0000000000..3a5f188e75
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -0,0 +1,891 @@
+# 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)
+
+
+# We use our own version of __repr__ when displaying the AST, as the
+# AST currently doesn't capture which nodes are reference (e.g. to
+# types) and which nodes are definitions. This allows us to e.g. print
+# the definition of a struct when it's defined inside a module, but
+# only print its name when it's referenced in e.g. a method parameter.
+def Repr(obj, as_ref=True):
+ """A version of __repr__ that can distinguish references.
+
+ Sometimes we like to print an object's full representation
+ (e.g. with its fields) and sometimes we just want to reference an
+ object that was printed in full elsewhere. This function allows us
+ to make that distinction.
+
+ Args:
+ obj: The object whose string representation we compute.
+ as_ref: If True, use the short reference representation.
+
+ Returns:
+ A str representation of |obj|.
+ """
+ if hasattr(obj, 'Repr'):
+ return obj.Repr(as_ref=as_ref)
+ # Since we cannot implement Repr for existing container types, we
+ # handle them here.
+ elif isinstance(obj, list):
+ if not obj:
+ return '[]'
+ else:
+ return ('[\n%s\n]' % (',\n'.join(' %s' % Repr(elem, as_ref).replace(
+ '\n', '\n ') for elem in obj)))
+ elif isinstance(obj, dict):
+ if not obj:
+ return '{}'
+ else:
+ return ('{\n%s\n}' % (',\n'.join(' %s: %s' % (
+ Repr(key, as_ref).replace('\n', '\n '),
+ Repr(val, as_ref).replace('\n', '\n '))
+ for key, val in obj.iteritems())))
+ else:
+ return repr(obj)
+
+
+def GenericRepr(obj, names):
+ """Compute generic Repr for |obj| based on the attributes in |names|.
+
+ Args:
+ obj: The object to compute a Repr for.
+ names: A dict from attribute names to include, to booleans
+ specifying whether those attributes should be shown as
+ references or not.
+
+ Returns:
+ A str representation of |obj|.
+ """
+ def ReprIndent(name, as_ref):
+ return ' %s=%s' % (name, Repr(getattr(obj, name), as_ref).replace(
+ '\n', '\n '))
+
+ return '%s(\n%s\n)' % (
+ obj.__class__.__name__,
+ ',\n'.join(ReprIndent(name, as_ref)
+ for (name, as_ref) in names.iteritems()))
+
+
+class Kind(object):
+ """Kind represents a type (e.g. int8, string).
+
+ Attributes:
+ spec: A string uniquely identifying the type. May be None.
+ parent_kind: The enclosing type. For example, a struct defined
+ inside an interface has that interface as its parent. May be None.
+ """
+ def __init__(self, spec=None):
+ self.spec = spec
+ self.parent_kind = None
+
+ def Repr(self, as_ref=True):
+ return '<%s spec=%r>' % (self.__class__.__name__, self.spec)
+
+ def __repr__(self):
+ # Gives us a decent __repr__ for all kinds.
+ return self.Repr()
+
+
+class ReferenceKind(Kind):
+ """ReferenceKind represents pointer 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.
+
+ Attributes:
+ is_nullable: True if the type is nullable.
+ """
+
+ 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 Repr(self, as_ref=True):
+ return '<%s spec=%r is_nullable=%r>' % (self.__class__.__name__, self.spec,
+ self.is_nullable)
+
+ 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
+)
+
+
+ATTRIBUTE_MIN_VERSION = 'MinVersion'
+ATTRIBUTE_EXTENSIBLE = 'Extensible'
+ATTRIBUTE_SYNC = 'Sync'
+
+
+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, parent_kind=None):
+ self.name = name
+ self.kind = kind
+ self.value = value
+ self.parent_kind = parent_kind
+
+
+class Field(object):
+ def __init__(self, name=None, kind=None, ordinal=None, default=None,
+ attributes=None):
+ if self.__class__.__name__ == 'Field':
+ raise Exception()
+ self.name = name
+ self.kind = kind
+ self.ordinal = ordinal
+ self.default = default
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ # Fields are only referenced by objects which define them and thus
+ # they are always displayed as non-references.
+ return GenericRepr(self, {'name': False, 'kind': True})
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+
+class StructField(Field): pass
+
+
+class UnionField(Field): pass
+
+
+class Struct(ReferenceKind):
+ """A struct with typed fields.
+
+ Attributes:
+ name: {str} The name of the struct type.
+ native_only: {bool} Does the struct have a body (i.e. any fields) or is it
+ purely a native struct.
+ module: {Module} The defining module.
+ imported_from: {dict} Information about where this union was
+ imported from.
+ fields: {List[StructField]} The members of the struct.
+ attributes: {dict} Additional information about the struct, such as
+ if it's a native struct.
+ """
+
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('native_only')
+ ReferenceKind.AddSharedProperty('module')
+ ReferenceKind.AddSharedProperty('imported_from')
+ ReferenceKind.AddSharedProperty('fields')
+ ReferenceKind.AddSharedProperty('attributes')
+
+ def __init__(self, name=None, module=None, attributes=None):
+ if name is not None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ ReferenceKind.__init__(self, spec)
+ self.name = name
+ self.native_only = False
+ self.module = module
+ self.imported_from = None
+ self.fields = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s name=%r imported_from=%s>' % (
+ self.__class__.__name__, self.name,
+ Repr(self.imported_from, as_ref=True))
+ else:
+ return GenericRepr(self, {'name': False, 'fields': False,
+ 'imported_from': True})
+
+ def AddField(self, name, kind, ordinal=None, default=None, attributes=None):
+ field = StructField(name, kind, ordinal, default, attributes)
+ self.fields.append(field)
+ return field
+
+
+class Union(ReferenceKind):
+ """A union of several kinds.
+
+ Attributes:
+ name: {str} The name of the union type.
+ module: {Module} The defining module.
+ imported_from: {dict} Information about where this union was
+ imported from.
+ fields: {List[UnionField]} The members of the union.
+ attributes: {dict} Additional information about the union, such as
+ which Java class name to use to represent it in the generated
+ bindings.
+ """
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('module')
+ ReferenceKind.AddSharedProperty('imported_from')
+ ReferenceKind.AddSharedProperty('fields')
+ ReferenceKind.AddSharedProperty('attributes')
+
+ def __init__(self, name=None, module=None, attributes=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 = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s spec=%r is_nullable=%r fields=%s>' % (
+ self.__class__.__name__, self.spec, self.is_nullable,
+ Repr(self.fields))
+ else:
+ return GenericRepr(self, {'fields': True, 'is_nullable': False})
+
+ def AddField(self, name, kind, ordinal=None, attributes=None):
+ field = UnionField(name, kind, ordinal, None, attributes)
+ self.fields.append(field)
+ return field
+
+
+class Array(ReferenceKind):
+ """An array.
+
+ Attributes:
+ kind: {Kind} The type of the elements. May be None.
+ length: The number of elements. None if unknown.
+ """
+
+ ReferenceKind.AddSharedProperty('kind')
+ ReferenceKind.AddSharedProperty('length')
+
+ def __init__(self, kind=None, length=None):
+ if kind is not None:
+ if length is not None:
+ spec = 'a%d:%s' % (length, kind.spec)
+ else:
+ spec = 'a:%s' % kind.spec
+
+ ReferenceKind.__init__(self, spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+ self.length = length
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s spec=%r is_nullable=%r kind=%s length=%r>' % (
+ self.__class__.__name__, self.spec, self.is_nullable, Repr(self.kind),
+ self.length)
+ else:
+ return GenericRepr(self, {'kind': True, 'length': False,
+ 'is_nullable': False})
+
+
+class Map(ReferenceKind):
+ """A map.
+
+ Attributes:
+ key_kind: {Kind} The type of the keys. May be None.
+ value_kind: {Kind} The type of the elements. May be None.
+ """
+ ReferenceKind.AddSharedProperty('key_kind')
+ ReferenceKind.AddSharedProperty('value_kind')
+
+ def __init__(self, key_kind=None, value_kind=None):
+ if (key_kind is not None and value_kind is not None):
+ ReferenceKind.__init__(self,
+ 'm[' + key_kind.spec + '][' + value_kind.spec +
+ ']')
+ if IsNullableKind(key_kind):
+ raise Exception("Nullable kinds cannot be keys in maps.")
+ if IsAnyHandleKind(key_kind):
+ raise Exception("Handles cannot be keys in maps.")
+ if IsAnyInterfaceKind(key_kind):
+ raise Exception("Interfaces cannot be keys in maps.")
+ if IsArrayKind(key_kind):
+ raise Exception("Arrays cannot be keys in maps.")
+ else:
+ ReferenceKind.__init__(self)
+
+ self.key_kind = key_kind
+ self.value_kind = value_kind
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s spec=%r is_nullable=%r key_kind=%s value_kind=%s>' % (
+ self.__class__.__name__, self.spec, self.is_nullable,
+ Repr(self.key_kind), Repr(self.value_kind))
+ else:
+ return GenericRepr(self, {'key_kind': True, 'value_kind': True})
+
+
+class InterfaceRequest(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ "Interface request requires %r to be an interface." % kind.spec)
+ ReferenceKind.__init__(self, 'r:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+
+class AssociatedInterfaceRequest(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, InterfaceRequest):
+ raise Exception(
+ "Associated interface request requires %r to be an interface "
+ "request." % kind.spec)
+ assert not kind.is_nullable
+ ReferenceKind.__init__(self, 'asso:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind.kind if kind is not None else None
+
+
+class Parameter(object):
+ def __init__(self, name=None, kind=None, ordinal=None, default=None,
+ attributes=None):
+ self.name = name
+ self.ordinal = ordinal
+ self.kind = kind
+ self.default = default
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ return '<%s name=%r kind=%s>' % (self.__class__.__name__, self.name,
+ self.kind.Repr(as_ref=True))
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+
+class Method(object):
+ def __init__(self, interface, name, ordinal=None, attributes=None):
+ self.interface = interface
+ self.name = name
+ self.ordinal = ordinal
+ self.parameters = []
+ self.response_parameters = None
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s name=%r>' % (self.__class__.__name__, self.name)
+ else:
+ return GenericRepr(self, {'name': False, 'parameters': True,
+ 'response_parameters': True})
+
+ def AddParameter(self, name, kind, ordinal=None, default=None,
+ attributes=None):
+ parameter = Parameter(name, kind, ordinal, default, attributes)
+ self.parameters.append(parameter)
+ return parameter
+
+ def AddResponseParameter(self, name, kind, ordinal=None, default=None,
+ attributes=None):
+ if self.response_parameters == None:
+ self.response_parameters = []
+ parameter = Parameter(name, kind, ordinal, default, attributes)
+ self.response_parameters.append(parameter)
+ return parameter
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+ @property
+ def sync(self):
+ return self.attributes.get(ATTRIBUTE_SYNC) \
+ if self.attributes else None
+
+
+class Interface(ReferenceKind):
+ ReferenceKind.AddSharedProperty('module')
+ ReferenceKind.AddSharedProperty('name')
+ ReferenceKind.AddSharedProperty('imported_from')
+ ReferenceKind.AddSharedProperty('methods')
+ ReferenceKind.AddSharedProperty('attributes')
+
+ def __init__(self, name=None, module=None, attributes=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.methods = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s name=%r>' % (self.__class__.__name__, self.name)
+ else:
+ return GenericRepr(self, {'name': False, 'attributes': False,
+ 'methods': False})
+
+ def AddMethod(self, name, ordinal=None, attributes=None):
+ method = Method(self, name, ordinal, attributes)
+ self.methods.append(method)
+ return method
+
+ # TODO(451323): Remove when the language backends no longer rely on this.
+ @property
+ def client(self):
+ return None
+
+
+class AssociatedInterface(ReferenceKind):
+ ReferenceKind.AddSharedProperty('kind')
+
+ def __init__(self, kind=None):
+ if kind is not None:
+ if not isinstance(kind, Interface):
+ raise Exception(
+ "Associated interface requires %r to be an interface." % kind.spec)
+ assert not kind.is_nullable
+ ReferenceKind.__init__(self, 'asso:' + kind.spec)
+ else:
+ ReferenceKind.__init__(self)
+ self.kind = kind
+
+
+class EnumField(object):
+ def __init__(self, name=None, value=None, attributes=None,
+ numeric_value=None):
+ self.name = name
+ self.value = value
+ self.attributes = attributes
+ self.numeric_value = numeric_value
+
+ @property
+ def min_version(self):
+ return self.attributes.get(ATTRIBUTE_MIN_VERSION) \
+ if self.attributes else None
+
+
+class Enum(Kind):
+ def __init__(self, name=None, module=None, attributes=None):
+ self.module = module
+ self.name = name
+ self.native_only = False
+ self.imported_from = None
+ if name is not None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ Kind.__init__(self, spec)
+ self.fields = []
+ self.attributes = attributes
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s name=%r>' % (self.__class__.__name__, self.name)
+ else:
+ return GenericRepr(self, {'name': False, 'fields': False})
+
+ @property
+ def extensible(self):
+ return self.attributes.get(ATTRIBUTE_EXTENSIBLE, False) \
+ if self.attributes else False
+
+
+class Module(object):
+ def __init__(self, name=None, namespace=None, attributes=None):
+ self.name = name
+ self.path = name
+ self.namespace = namespace
+ self.structs = []
+ self.unions = []
+ self.interfaces = []
+ self.kinds = {}
+ self.attributes = attributes
+
+ def __repr__(self):
+ # Gives us a decent __repr__ for modules.
+ return self.Repr()
+
+ def Repr(self, as_ref=True):
+ if as_ref:
+ return '<%s name=%r namespace=%r>' % (
+ self.__class__.__name__, self.name, self.namespace)
+ else:
+ return GenericRepr(self, {'name': False, 'namespace': False,
+ 'attributes': False, 'structs': False,
+ 'interfaces': False, 'unions': False})
+
+ def AddInterface(self, name, attributes=None):
+ interface = Interface(name, self, attributes)
+ self.interfaces.append(interface)
+ return interface
+
+ def AddStruct(self, name, attributes=None):
+ struct = Struct(name, self, attributes)
+ self.structs.append(struct)
+ return struct
+
+ def AddUnion(self, name, attributes=None):
+ union = Union(name, self, attributes)
+ self.unions.append(union)
+ return union
+
+
+def IsBoolKind(kind):
+ return kind.spec == BOOL.spec
+
+
+def IsFloatKind(kind):
+ return kind.spec == FLOAT.spec
+
+
+def IsDoubleKind(kind):
+ return kind.spec == DOUBLE.spec
+
+
+def IsIntegralKind(kind):
+ return (kind.spec == BOOL.spec or
+ kind.spec == INT8.spec or
+ kind.spec == INT16.spec or
+ kind.spec == INT32.spec or
+ kind.spec == INT64.spec or
+ kind.spec == UINT8.spec or
+ kind.spec == UINT16.spec or
+ kind.spec == UINT32.spec or
+ kind.spec == UINT64.spec)
+
+
+def IsStringKind(kind):
+ return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec
+
+
+def IsGenericHandleKind(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 IsUnionKind(kind):
+ return isinstance(kind, Union)
+
+
+def IsArrayKind(kind):
+ return isinstance(kind, Array)
+
+
+def IsInterfaceKind(kind):
+ return isinstance(kind, Interface)
+
+
+def IsAssociatedInterfaceKind(kind):
+ return isinstance(kind, AssociatedInterface)
+
+
+def IsInterfaceRequestKind(kind):
+ return isinstance(kind, InterfaceRequest)
+
+
+def IsAssociatedInterfaceRequestKind(kind):
+ return isinstance(kind, AssociatedInterfaceRequest)
+
+
+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 IsMapKind(kind):
+ return isinstance(kind, Map)
+
+
+def IsObjectKind(kind):
+ return IsPointerKind(kind) or IsUnionKind(kind)
+
+
+def IsPointerKind(kind):
+ return (IsStructKind(kind) or IsArrayKind(kind) or IsStringKind(kind) or
+ IsMapKind(kind))
+
+
+# Please note that it doesn't include any interface kind.
+def IsAnyHandleKind(kind):
+ return (IsGenericHandleKind(kind) or
+ IsDataPipeConsumerKind(kind) or
+ IsDataPipeProducerKind(kind) or
+ IsMessagePipeKind(kind) or
+ IsSharedBufferKind(kind))
+
+
+def IsAnyInterfaceKind(kind):
+ return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind) or
+ IsAssociatedKind(kind))
+
+
+def IsAnyHandleOrInterfaceKind(kind):
+ return IsAnyHandleKind(kind) or IsAnyInterfaceKind(kind)
+
+
+def IsAssociatedKind(kind):
+ return (IsAssociatedInterfaceKind(kind) or
+ IsAssociatedInterfaceRequestKind(kind))
+
+
+def HasCallbacks(interface):
+ for method in interface.methods:
+ if method.response_parameters != None:
+ return True
+ return False
+
+
+# Finds out whether an interface passes associated interfaces and associated
+# interface requests.
+def PassesAssociatedKinds(interface):
+ def _ContainsAssociatedKinds(kind, visited_kinds):
+ if kind in visited_kinds:
+ # No need to examine the kind again.
+ return False
+ visited_kinds.add(kind)
+ if IsAssociatedKind(kind):
+ return True
+ if IsArrayKind(kind):
+ return _ContainsAssociatedKinds(kind.kind, visited_kinds)
+ if IsStructKind(kind) or IsUnionKind(kind):
+ for field in kind.fields:
+ if _ContainsAssociatedKinds(field.kind, visited_kinds):
+ return True
+ if IsMapKind(kind):
+ # No need to examine the key kind, only primitive kinds and non-nullable
+ # string are allowed to be key kinds.
+ return _ContainsAssociatedKinds(kind.value_kind, visited_kinds)
+ return False
+
+ visited_kinds = set()
+ for method in interface.methods:
+ for param in method.parameters:
+ if _ContainsAssociatedKinds(param.kind, visited_kinds):
+ return True
+ if method.response_parameters != None:
+ for param in method.response_parameters:
+ if _ContainsAssociatedKinds(param.kind, visited_kinds):
+ return True
+ return False
+
+
+def HasSyncMethods(interface):
+ for method in interface.methods:
+ if method.sync:
+ return True
+ return False
+
+
+def ContainsHandlesOrInterfaces(kind):
+ """Check if the kind contains any handles.
+
+ This check is recursive so it checks all struct fields, containers elements,
+ etc.
+
+ Args:
+ struct: {Kind} The kind to check.
+
+ Returns:
+ {bool}: True if the kind contains handles.
+ """
+ # We remember the types we already checked to avoid infinite recursion when
+ # checking recursive (or mutually recursive) types:
+ checked = set()
+ def Check(kind):
+ if kind.spec in checked:
+ return False
+ checked.add(kind.spec)
+ if IsStructKind(kind):
+ return any(Check(field.kind) for field in kind.fields)
+ elif IsUnionKind(kind):
+ return any(Check(field.kind) for field in kind.fields)
+ elif IsAnyHandleKind(kind):
+ return True
+ elif IsAnyInterfaceKind(kind):
+ return True
+ elif IsArrayKind(kind):
+ return Check(kind.kind)
+ elif IsMapKind(kind):
+ return Check(kind.key_kind) or Check(kind.value_kind)
+ else:
+ return False
+ return Check(kind)
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 0000000000..a887686e1b
--- /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 0000000000..37dc8f396b
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
@@ -0,0 +1,250 @@
+# 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).
+
+# Size of struct header in bytes: num_bytes [4B] + version [4B].
+HEADER_SIZE = 8
+
+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.Map, mojom.Struct,
+ mojom.Interface, mojom.AssociatedInterface)):
+ return 8
+ if isinstance(kind, mojom.Union):
+ return 16
+ if isinstance(kind, mojom.InterfaceRequest):
+ kind = mojom.MSGPIPE
+ if isinstance(kind, mojom.AssociatedInterfaceRequest):
+ return 4
+ 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]
+
+ @classmethod
+ def GetAlignmentForKind(cls, kind):
+ if isinstance(kind, (mojom.Interface, mojom.AssociatedInterface)):
+ return 4
+ if isinstance(kind, mojom.Union):
+ return 8
+ return cls.GetSizeForKind(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.alignment = self.GetAlignmentForKind(field.kind)
+ self.offset = None
+ self.bit = None
+ self.min_version = None
+
+
+def GetPad(offset, alignment):
+ """Returns the pad necessary to reserve space so that |offset + pad| equals to
+ some multiple of |alignment|."""
+ return (alignment - (offset % alignment)) % alignment
+
+
+def GetFieldOffset(field, last_field):
+ """Returns a 2-tuple of the field offset and bit (for BOOLs)."""
+ 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.alignment)
+ return (offset + pad, 0)
+
+
+def GetPayloadSizeUpToField(field):
+ """Returns the payload size (not including struct header) if |field| is the
+ last field.
+ """
+ if not field:
+ return 0
+ offset = field.offset + field.size
+ pad = GetPad(offset, 8)
+ return offset + pad
+
+
+class PackedStruct(object):
+ def __init__(self, struct):
+ self.struct = struct
+ # |packed_fields| contains all the fields, in increasing offset order.
+ self.packed_fields = []
+ # |packed_fields_in_ordinal_order| refers to the same fields as
+ # |packed_fields|, but in ordinal order.
+ self.packed_fields_in_ordinal_order = []
+
+ # No fields.
+ if (len(struct.fields) == 0):
+ return
+
+ # Start by sorting by ordinal.
+ src_fields = self.packed_fields_in_ordinal_order
+ 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)
+
+ # Set |min_version| for each field.
+ next_min_version = 0
+ for packed_field in src_fields:
+ if packed_field.field.min_version is None:
+ assert next_min_version == 0
+ else:
+ assert packed_field.field.min_version >= next_min_version
+ next_min_version = packed_field.field.min_version
+ packed_field.min_version = next_min_version
+
+ if (packed_field.min_version != 0 and
+ mojom.IsReferenceKind(packed_field.field.kind) and
+ not packed_field.field.kind.is_nullable):
+ raise Exception("Non-nullable fields are only allowed in version 0 of "
+ "a struct. %s.%s is defined with [MinVersion=%d]."
+ % (self.struct.name, packed_field.field.name,
+ packed_field.min_version))
+
+ src_field = src_fields[0]
+ src_field.offset = 0
+ src_field.bit = 0
+ 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)
+
+
+class ByteInfo(object):
+ def __init__(self):
+ self.is_padding = False
+ self.packed_fields = []
+
+
+def GetByteLayout(packed_struct):
+ total_payload_size = GetPayloadSizeUpToField(
+ packed_struct.packed_fields[-1] if packed_struct.packed_fields else None)
+ bytes = [ByteInfo() for i in xrange(total_payload_size)]
+
+ 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
+
+
+class VersionInfo(object):
+ def __init__(self, version, num_fields, num_bytes):
+ self.version = version
+ self.num_fields = num_fields
+ self.num_bytes = num_bytes
+
+
+def GetVersionInfo(packed_struct):
+ """Get version information for a struct.
+
+ Args:
+ packed_struct: A PackedStruct instance.
+
+ Returns:
+ A non-empty list of VersionInfo instances, sorted by version in increasing
+ order.
+ Note: The version numbers may not be consecutive.
+ """
+ versions = []
+ last_version = 0
+ last_num_fields = 0
+ last_payload_size = 0
+
+ for packed_field in packed_struct.packed_fields_in_ordinal_order:
+ if packed_field.min_version != last_version:
+ versions.append(
+ VersionInfo(last_version, last_num_fields,
+ last_payload_size + HEADER_SIZE))
+ last_version = packed_field.min_version
+
+ last_num_fields += 1
+ # The fields are iterated in ordinal order here. However, the size of a
+ # version is determined by the last field of that version in pack order,
+ # instead of ordinal order. Therefore, we need to calculate the max value.
+ last_payload_size = max(GetPayloadSizeUpToField(packed_field),
+ last_payload_size)
+
+ assert len(versions) == 0 or last_num_fields != versions[-1].num_fields
+ versions.append(VersionInfo(last_version, last_num_fields,
+ last_payload_size + HEADER_SIZE))
+ return versions
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 0000000000..14f699da34
--- /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.Array(length=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 0000000000..41f11a2b71
--- /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 0000000000..66f8954012
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -0,0 +1,67 @@
+# 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 third_party/WebKit/Source/build/scripts/template_expander.py.
+
+import imp
+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, path_to_template, params, **kwargs):
+ loader = jinja2.ModuleLoader(os.path.join(
+ mojo_generator.bytecode_path, "%s.zip" % mojo_generator.GetTemplatePrefix(
+ )))
+ 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())
+ jinja_env.filters.update(mojo_generator.GetFilters())
+ template = jinja_env.get_template(path_to_template)
+ return template.render(params)
+
+
+def UseJinja(path_to_template, **kwargs):
+ def RealDecorator(generator):
+ def GeneratorInternal(*args, **kwargs2):
+ parameters = generator(*args, **kwargs2)
+ return ApplyTemplate(args[0], path_to_template, parameters, **kwargs)
+ GeneratorInternal.func_name = generator.func_name
+ return GeneratorInternal
+ return RealDecorator
+
+
+def PrecompileTemplates(generator_modules, output_dir):
+ for module in generator_modules.values():
+ generator = module.Generator(None)
+ jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader([os.path.join(
+ os.path.dirname(module.__file__), generator.GetTemplatePrefix())]))
+ jinja_env.filters.update(generator.GetFilters())
+ jinja_env.compile_templates(
+ os.path.join(output_dir, "%s.zip" % generator.GetTemplatePrefix()),
+ extensions=["tmpl"],
+ zip="stored",
+ py_compile=True,
+ ignore_errors=False)
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 0000000000..eb394619d2
--- /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/generate/translate.py b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
new file mode 100644
index 0000000000..ffad7447a9
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
@@ -0,0 +1,639 @@
+# 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.
+
+"""Convert parse tree to AST.
+
+This module converts the parse tree to the AST we use for code generation. The
+main entry point is OrderedModule, which gets passed the parser
+representation of a mojom file. When called it's assumed that all imports have
+already been parsed and converted to ASTs before.
+"""
+
+import copy
+import re
+
+import module as mojom
+from mojom.parse import ast
+
+def _DuplicateName(values):
+ """Returns the 'name' of the first entry in |values| whose 'name' has already
+ been encountered. If there are no duplicates, returns None."""
+ names = set()
+ for value in values:
+ if value.name in names:
+ return value.name
+ names.add(value.name)
+ return None
+
+def _ElemsOfType(elems, elem_type, scope):
+ """Find all elements of the given type.
+
+ Args:
+ elems: {Sequence[Any]} Sequence of elems.
+ elem_type: {Type[C]} Extract all elems of this type.
+ scope: {str} The name of the surrounding scope (e.g. struct
+ definition). Used in error messages.
+
+ Returns:
+ {List[C]} All elems of matching type.
+ """
+ assert isinstance(elem_type, type)
+ result = [elem for elem in elems if isinstance(elem, elem_type)]
+ duplicate_name = _DuplicateName(result)
+ if duplicate_name:
+ raise Exception('Names in mojom must be unique within a scope. The name '
+ '"%s" is used more than once within the scope "%s".' %
+ (duplicate_name, scope))
+ return result
+
+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 = ('m', 's', 'h', 'a', 'r', 'x', 'asso')
+ if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
+ raise Exception(
+ 'A type (spec "%s") cannot be made nullable' % base_kind)
+ return '?' + base_kind
+ if kind.endswith('}'):
+ lbracket = kind.rfind('{')
+ value = kind[0:lbracket]
+ return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']'
+ if kind.endswith(']'):
+ lbracket = kind.rfind('[')
+ typename = kind[0:lbracket]
+ return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
+ if kind.endswith('&'):
+ return 'r:' + _MapKind(kind[0:-1])
+ if kind.startswith('asso<'):
+ assert kind.endswith('>')
+ return 'asso:' + _MapKind(kind[5:-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 None
+ assert isinstance(attribute_list, ast.AttributeList)
+ # TODO(vtl): Check for duplicate keys here.
+ return dict([(attribute.key, attribute.value)
+ for attribute in attribute_list])
+
+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 _Kind(kinds, spec, scope):
+ """Convert a type name into a mojom.Kind object.
+
+ As a side-effect this function adds the result to 'kinds'.
+
+ Args:
+ kinds: {Dict[str, mojom.Kind]} All known kinds up to this point, indexed by
+ their names.
+ spec: {str} A name uniquely identifying a type.
+ scope: {Tuple[str, str]} A tuple that looks like (namespace,
+ struct/interface), referring to the location where the type is
+ referenced.
+
+ Returns:
+ {mojom.Kind} The type corresponding to 'spec'.
+ """
+ kind = _LookupKind(kinds, spec, scope)
+ if kind:
+ return kind
+
+ if spec.startswith('?'):
+ kind = _Kind(kinds, spec[1:], scope).MakeNullableKind()
+ elif spec.startswith('a:'):
+ kind = mojom.Array(_Kind(kinds, spec[2:], scope))
+ elif spec.startswith('asso:'):
+ inner_kind = _Kind(kinds, spec[5:], scope)
+ if isinstance(inner_kind, mojom.InterfaceRequest):
+ kind = mojom.AssociatedInterfaceRequest(inner_kind)
+ else:
+ kind = mojom.AssociatedInterface(inner_kind)
+ elif spec.startswith('a'):
+ colon = spec.find(':')
+ length = int(spec[1:colon])
+ kind = mojom.Array(_Kind(kinds, spec[colon+1:], scope), length)
+ elif spec.startswith('r:'):
+ kind = mojom.InterfaceRequest(_Kind(kinds, spec[2:], scope))
+ elif spec.startswith('m['):
+ # Isolate the two types from their brackets.
+
+ # It is not allowed to use map as key, so there shouldn't be nested ']'s
+ # inside the key type spec.
+ key_end = spec.find(']')
+ assert key_end != -1 and key_end < len(spec) - 1
+ assert spec[key_end+1] == '[' and spec[-1] == ']'
+
+ first_kind = spec[2:key_end]
+ second_kind = spec[key_end+2:-1]
+
+ kind = mojom.Map(_Kind(kinds, first_kind, scope),
+ _Kind(kinds, second_kind, scope))
+ else:
+ kind = mojom.Kind(spec)
+
+ kinds[spec] = 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, Unions, 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 _Import(module, import_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.
+ importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface)
+ for kind in import_module.kinds.itervalues():
+ if (isinstance(kind, importable_kinds) 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 _Struct(module, parsed_struct):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_struct: {ast.Struct} Parsed struct.
+
+ Returns:
+ {mojom.Struct} AST struct.
+ """
+ struct = mojom.Struct(module=module)
+ struct.name = parsed_struct.name
+ struct.native_only = parsed_struct.body is None
+ struct.spec = 'x:' + module.namespace + '.' + struct.name
+ module.kinds[struct.spec] = struct
+ if struct.native_only:
+ struct.enums = []
+ struct.constants = []
+ struct.fields_data = []
+ else:
+ struct.enums = map(
+ lambda enum: _Enum(module, enum, struct),
+ _ElemsOfType(parsed_struct.body, ast.Enum, parsed_struct.name))
+ struct.constants = map(
+ lambda constant: _Constant(module, constant, struct),
+ _ElemsOfType(parsed_struct.body, ast.Const, parsed_struct.name))
+ # Stash fields parsed_struct here temporarily.
+ struct.fields_data = _ElemsOfType(
+ parsed_struct.body, ast.StructField, parsed_struct.name)
+ struct.attributes = _AttributeListToDict(parsed_struct.attribute_list)
+
+ # Enforce that a [Native] attribute is set to make native-only struct
+ # declarations more explicit.
+ if struct.native_only:
+ if not struct.attributes or not struct.attributes.get('Native', False):
+ raise Exception("Native-only struct declarations must include a " +
+ "Native attribute.")
+
+ return struct
+
+def _Union(module, parsed_union):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_union: {ast.Union} Parsed union.
+
+ Returns:
+ {mojom.Union} AST union.
+ """
+ union = mojom.Union(module=module)
+ union.name = parsed_union.name
+ union.spec = 'x:' + module.namespace + '.' + union.name
+ module.kinds[union.spec] = union
+ # Stash fields parsed_union here temporarily.
+ union.fields_data = _ElemsOfType(
+ parsed_union.body, ast.UnionField, parsed_union.name)
+ union.attributes = _AttributeListToDict(parsed_union.attribute_list)
+ return union
+
+def _StructField(module, parsed_field, struct):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_field: {ast.StructField} Parsed struct field.
+ struct: {mojom.Struct} Struct this field belongs to.
+
+ Returns:
+ {mojom.StructField} AST struct field.
+ """
+ field = mojom.StructField()
+ field.name = parsed_field.name
+ field.kind = _Kind(
+ module.kinds, _MapKind(parsed_field.typename),
+ (module.namespace, struct.name))
+ field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
+ field.default = _FixupExpression(
+ module, parsed_field.default_value, (module.namespace, struct.name),
+ field.kind)
+ field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+ return field
+
+def _UnionField(module, parsed_field, union):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_field: {ast.UnionField} Parsed union field.
+ union: {mojom.Union} Union this fields belong to.
+
+ Returns:
+ {mojom.UnionField} AST union.
+ """
+ field = mojom.UnionField()
+ field.name = parsed_field.name
+ field.kind = _Kind(
+ module.kinds, _MapKind(parsed_field.typename),
+ (module.namespace, union.name))
+ field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
+ field.default = _FixupExpression(
+ module, None, (module.namespace, union.name), field.kind)
+ field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+ return field
+
+def _Parameter(module, parsed_param, interface):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_param: {ast.Parameter} Parsed parameter.
+ union: {mojom.Interface} Interface this parameter belongs to.
+
+ Returns:
+ {mojom.Parameter} AST parameter.
+ """
+ parameter = mojom.Parameter()
+ parameter.name = parsed_param.name
+ parameter.kind = _Kind(
+ module.kinds, _MapKind(parsed_param.typename),
+ (module.namespace, interface.name))
+ parameter.ordinal = (
+ parsed_param.ordinal.value if parsed_param.ordinal else None)
+ parameter.default = None # TODO(tibell): We never have these. Remove field?
+ parameter.attributes = _AttributeListToDict(parsed_param.attribute_list)
+ return parameter
+
+def _Method(module, parsed_method, interface):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_method: {ast.Method} Parsed method.
+ interface: {mojom.Interface} Interface this method belongs to.
+
+ Returns:
+ {mojom.Method} AST method.
+ """
+ method = mojom.Method(
+ interface, parsed_method.name,
+ ordinal=parsed_method.ordinal.value if parsed_method.ordinal else None)
+ method.parameters = map(
+ lambda parameter: _Parameter(module, parameter, interface),
+ parsed_method.parameter_list)
+ if parsed_method.response_parameter_list is not None:
+ method.response_parameters = map(
+ lambda parameter: _Parameter(module, parameter, interface),
+ parsed_method.response_parameter_list)
+ method.attributes = _AttributeListToDict(parsed_method.attribute_list)
+
+ # Enforce that only methods with response can have a [Sync] attribute.
+ if method.sync and method.response_parameters is None:
+ raise Exception("Only methods with response can include a [Sync] "
+ "attribute. If no response parameters are needed, you "
+ "could use an empty response parameter list, i.e., "
+ "\"=> ()\".")
+
+ return method
+
+def _Interface(module, parsed_iface):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_iface: {ast.Interface} Parsed interface.
+
+ Returns:
+ {mojom.Interface} AST interface.
+ """
+ interface = mojom.Interface(module=module)
+ interface.name = parsed_iface.name
+ interface.spec = 'x:' + module.namespace + '.' + interface.name
+ module.kinds[interface.spec] = interface
+ interface.enums = map(
+ lambda enum: _Enum(module, enum, interface),
+ _ElemsOfType(parsed_iface.body, ast.Enum, parsed_iface.name))
+ interface.constants = map(
+ lambda constant: _Constant(module, constant, interface),
+ _ElemsOfType(parsed_iface.body, ast.Const, parsed_iface.name))
+ # Stash methods parsed_iface here temporarily.
+ interface.methods_data = _ElemsOfType(
+ parsed_iface.body, ast.Method, parsed_iface.name)
+ interface.attributes = _AttributeListToDict(parsed_iface.attribute_list)
+ return interface
+
+def _EnumField(module, enum, parsed_field, parent_kind):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ enum: {mojom.Enum} Enum this field belongs to.
+ parsed_field: {ast.EnumValue} Parsed enum value.
+ parent_kind: {mojom.Kind} The enclosing type.
+
+ Returns:
+ {mojom.EnumField} AST enum field.
+ """
+ field = mojom.EnumField()
+ field.name = parsed_field.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, parsed_field.value, (module.namespace, parent_kind.name), enum)
+ else:
+ field.value = _FixupExpression(
+ module, parsed_field.value, (module.namespace, ), enum)
+ field.attributes = _AttributeListToDict(parsed_field.attribute_list)
+ value = mojom.EnumValue(module, enum, field)
+ module.values[value.GetSpec()] = value
+ return field
+
+def _ResolveNumericEnumValues(enum_fields):
+ """
+ Given a reference to a list of mojom.EnumField, resolves and assigns their
+ values to EnumField.numeric_value.
+ """
+
+ # map of <name> -> integral value
+ resolved_enum_values = {}
+ prev_value = -1
+ for field in enum_fields:
+ # This enum value is +1 the previous enum value (e.g: BEGIN).
+ if field.value is None:
+ prev_value += 1
+
+ # Integral value (e.g: BEGIN = -0x1).
+ elif type(field.value) is str:
+ prev_value = int(field.value, 0)
+
+ # Reference to a previous enum value (e.g: INIT = BEGIN).
+ elif type(field.value) is mojom.EnumValue:
+ prev_value = resolved_enum_values[field.value.name]
+ else:
+ raise Exception("Unresolved enum value.")
+
+ resolved_enum_values[field.name] = prev_value
+ field.numeric_value = prev_value
+
+def _Enum(module, parsed_enum, parent_kind):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_enum: {ast.Enum} Parsed enum.
+
+ Returns:
+ {mojom.Enum} AST enum.
+ """
+ enum = mojom.Enum(module=module)
+ enum.name = parsed_enum.name
+ enum.native_only = parsed_enum.enum_value_list is None
+ 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.attributes = _AttributeListToDict(parsed_enum.attribute_list)
+ if enum.native_only:
+ enum.fields = []
+ else:
+ enum.fields = map(
+ lambda field: _EnumField(module, enum, field, parent_kind),
+ parsed_enum.enum_value_list)
+ _ResolveNumericEnumValues(enum.fields)
+
+ module.kinds[enum.spec] = enum
+
+ # Enforce that a [Native] attribute is set to make native-only enum
+ # declarations more explicit.
+ if enum.native_only:
+ if not enum.attributes or not enum.attributes.get('Native', False):
+ raise Exception("Native-only enum declarations must include a " +
+ "Native attribute.")
+
+ return enum
+
+def _Constant(module, parsed_const, parent_kind):
+ """
+ Args:
+ module: {mojom.Module} Module currently being constructed.
+ parsed_const: {ast.Const} Parsed constant.
+
+ Returns:
+ {mojom.Constant} AST constant.
+ """
+ constant = mojom.Constant()
+ constant.name = parsed_const.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 = _Kind(module.kinds, _MapKind(parsed_const.typename), scope)
+ constant.parent_kind = parent_kind
+ constant.value = _FixupExpression(module, parsed_const.value, scope, None)
+
+ value = mojom.ConstantValue(module, parent_kind, constant)
+ module.values[value.GetSpec()] = value
+ return constant
+
+def _Module(tree, name, imports):
+ """
+ Args:
+ tree: {ast.Mojom} The parse tree.
+ name: {str} The mojom filename, excluding the path.
+ imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in
+ the import list, to already processed modules. Used to process imports.
+
+ Returns:
+ {mojom.Module} An AST for the mojom.
+ """
+ module = mojom.Module()
+ module.kinds = {}
+ for kind in mojom.PRIMITIVES:
+ module.kinds[kind.spec] = kind
+
+ module.values = {}
+
+ module.name = name
+ module.namespace = tree.module.name[1] if tree.module else ''
+ # Imports must come first, because they add to module.kinds which is used
+ # by by the others.
+ module.imports = [
+ _Import(module, imports[imp.import_filename])
+ for imp in tree.import_list]
+ if tree.module and tree.module.attribute_list:
+ assert isinstance(tree.module.attribute_list, ast.AttributeList)
+ # TODO(vtl): Check for duplicate keys here.
+ module.attributes = dict((attribute.key, attribute.value)
+ for attribute in tree.module.attribute_list)
+
+ # First pass collects kinds.
+ module.enums = map(
+ lambda enum: _Enum(module, enum, None),
+ _ElemsOfType(tree.definition_list, ast.Enum, name))
+ module.structs = map(
+ lambda struct: _Struct(module, struct),
+ _ElemsOfType(tree.definition_list, ast.Struct, name))
+ module.unions = map(
+ lambda union: _Union(module, union),
+ _ElemsOfType(tree.definition_list, ast.Union, name))
+ module.interfaces = map(
+ lambda interface: _Interface(module, interface),
+ _ElemsOfType(tree.definition_list, ast.Interface, name))
+ module.constants = map(
+ lambda constant: _Constant(module, constant, None),
+ _ElemsOfType(tree.definition_list, ast.Const, name))
+
+ # 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:
+ _StructField(module, field, struct), struct.fields_data)
+ del struct.fields_data
+ for union in module.unions:
+ union.fields = map(lambda field:
+ _UnionField(module, field, union), union.fields_data)
+ del union.fields_data
+ for interface in module.interfaces:
+ interface.methods = map(lambda method:
+ _Method(module, method, interface), interface.methods_data)
+ del interface.methods_data
+
+ return module
+
+def OrderedModule(tree, name, imports):
+ """Convert parse tree to AST module.
+
+ Args:
+ tree: {ast.Mojom} The parse tree.
+ name: {str} The mojom filename, excluding the path.
+ imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in
+ the import list, to already processed modules. Used to process imports.
+
+ Returns:
+ {mojom.Module} An AST for the mojom.
+ """
+ module = _Module(tree, name, imports)
+ 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/parse/__init__.py b/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /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 0000000000..2c6b5fcdf8
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -0,0 +1,410 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""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, attribute_list, enum_value_list, **kwargs):
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert enum_value_list is None or isinstance(enum_value_list, EnumValueList)
+ super(Enum, self).__init__(name, **kwargs)
+ self.attribute_list = attribute_list
+ self.enum_value_list = enum_value_list
+
+ def __eq__(self, other):
+ return super(Enum, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list and \
+ self.enum_value_list == other.enum_value_list
+
+
+class EnumValue(Definition):
+ """Represents a definition of an enum value."""
+
+ def __init__(self, name, attribute_list, value, **kwargs):
+ # The optional value is either an int (which is current a string) or a
+ # "wrapped identifier".
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert value is None or isinstance(value, (str, tuple))
+ super(EnumValue, self).__init__(name, **kwargs)
+ self.attribute_list = attribute_list
+ self.value = value
+
+ def __eq__(self, other):
+ return super(EnumValue, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list 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, attribute_list, ordinal, parameter_list,
+ response_parameter_list, **kwargs):
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ 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.attribute_list = attribute_list
+ 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.attribute_list == other.attribute_list 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, attribute_list, ordinal, typename, **kwargs):
+ assert isinstance(name, str)
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert ordinal is None or isinstance(ordinal, Ordinal)
+ assert isinstance(typename, str)
+ super(Parameter, self).__init__(**kwargs)
+ self.name = name
+ self.attribute_list = attribute_list
+ self.ordinal = ordinal
+ self.typename = typename
+
+ def __eq__(self, other):
+ return super(Parameter, self).__eq__(other) and \
+ self.name == other.name and \
+ self.attribute_list == other.attribute_list 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) or body is None
+ 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, attribute_list, ordinal, typename, default_value,
+ **kwargs):
+ assert isinstance(name, str)
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ 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.attribute_list = attribute_list
+ self.ordinal = ordinal
+ self.typename = typename
+ self.default_value = default_value
+
+ def __eq__(self, other):
+ return super(StructField, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list 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)
+
+
+class Union(Definition):
+ """Represents a union definition."""
+
+ def __init__(self, name, attribute_list, body, **kwargs):
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert isinstance(body, UnionBody)
+ super(Union, self).__init__(name, **kwargs)
+ self.attribute_list = attribute_list
+ self.body = body
+
+ def __eq__(self, other):
+ return super(Union, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list and \
+ self.body == other.body
+
+
+class UnionField(Definition):
+
+ def __init__(self, name, attribute_list, ordinal, typename, **kwargs):
+ assert isinstance(name, str)
+ assert attribute_list is None or isinstance(attribute_list, AttributeList)
+ assert ordinal is None or isinstance(ordinal, Ordinal)
+ assert isinstance(typename, str)
+ super(UnionField, self).__init__(name, **kwargs)
+ self.attribute_list = attribute_list
+ self.ordinal = ordinal
+ self.typename = typename
+
+ def __eq__(self, other):
+ return super(UnionField, self).__eq__(other) and \
+ self.attribute_list == other.attribute_list and \
+ self.ordinal == other.ordinal and \
+ self.typename == other.typename
+
+
+class UnionBody(NodeListBase):
+
+ _list_item_type = UnionField
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 0000000000..06354b1d85
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
@@ -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.
+
+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',
+ 'UNION',
+ 'INTERFACE',
+ 'ENUM',
+ 'CONST',
+ 'TRUE',
+ 'FALSE',
+ 'DEFAULT',
+ 'ARRAY',
+ 'MAP',
+ 'ASSOCIATED'
+ )
+
+ 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 0000000000..868fb45f33
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -0,0 +1,461 @@
+# Copyright 2014 The Chromium 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 : """
+ p[0] = ast.Mojom(None, ast.ImportList(), [])
+
+ def p_root_2(self, p):
+ """root : root module"""
+ if p[1].module is not None:
+ raise ParseError(self.filename,
+ "Multiple \"module\" statements not allowed:",
+ p[2].lineno, snippet=self._GetSnippet(p[2].lineno))
+ if p[1].import_list.items or p[1].definition_list:
+ raise ParseError(
+ self.filename,
+ "\"module\" statements must precede imports and definitions:",
+ p[2].lineno, snippet=self._GetSnippet(p[2].lineno))
+ p[0] = p[1]
+ p[0].module = p[2]
+
+ def p_root_3(self, p):
+ """root : root import"""
+ if p[1].definition_list:
+ raise ParseError(self.filename,
+ "\"import\" statements must precede definitions:",
+ p[2].lineno, snippet=self._GetSnippet(p[2].lineno))
+ p[0] = p[1]
+ p[0].import_list.Append(p[2])
+
+ def p_root_4(self, p):
+ """root : root definition"""
+ p[0] = p[1]
+ p[0].definition_list.append(p[2])
+
+ def p_import(self, p):
+ """import : IMPORT STRING_LITERAL SEMI"""
+ # '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]), filename=self.filename, lineno=p.lineno(2))
+
+ def p_module(self, p):
+ """module : attribute_section MODULE identifier_wrapped SEMI"""
+ p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2))
+
+ def p_definition(self, p):
+ """definition : struct
+ | union
+ | 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_1(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_attribute_2(self, p):
+ """attribute : NAME"""
+ p[0] = ast.Attribute(p[1], True, filename=self.filename, lineno=p.lineno(1))
+
+ def p_evaled_literal(self, p):
+ """evaled_literal : literal"""
+ # 'eval' the literal to strip the quotes. Handle keywords "true" and "false"
+ # specially since they cannot directly be evaluated to python boolean
+ # values.
+ if p[1] == "true":
+ p[0] = True
+ elif p[1] == "false":
+ p[0] = False
+ else:
+ p[0] = eval(p[1])
+
+ def p_struct_1(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_2(self, p):
+ """struct : attribute_section STRUCT NAME SEMI"""
+ p[0] = ast.Struct(p[3], p[1], None)
+
+ 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 : attribute_section typename NAME ordinal default SEMI"""
+ p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5])
+
+ def p_union(self, p):
+ """union : attribute_section UNION NAME LBRACE union_body RBRACE SEMI"""
+ p[0] = ast.Union(p[3], p[1], p[5])
+
+ def p_union_body_1(self, p):
+ """union_body : """
+ p[0] = ast.UnionBody()
+
+ def p_union_body_2(self, p):
+ """union_body : union_body union_field"""
+ p[0] = p[1]
+ p[1].Append(p[2])
+
+ def p_union_field(self, p):
+ """union_field : attribute_section typename NAME ordinal SEMI"""
+ p[0] = ast.UnionField(p[3], p[1], p[4], p[2])
+
+ 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 : attribute_section NAME ordinal LPAREN parameter_list RPAREN \
+ response SEMI"""
+ p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7])
+
+ 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 : attribute_section typename NAME ordinal"""
+ p[0] = ast.Parameter(p[3], p[1], p[4], p[2],
+ filename=self.filename, lineno=p.lineno(3))
+
+ 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
+ | associative_array
+ | interfacerequest"""
+ p[0] = p[1]
+
+ def p_basictypename(self, p):
+ """basictypename : identifier
+ | ASSOCIATED identifier
+ | handletype"""
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ p[0] = "asso<" + p[2] + ">"
+
+ 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_associative_array(self, p):
+ """associative_array : MAP LANGLE identifier COMMA typename RANGLE"""
+ p[0] = p[5] + "{" + p[3] + "}"
+
+ def p_interfacerequest(self, p):
+ """interfacerequest : identifier AMP
+ | ASSOCIATED identifier AMP"""
+ if len(p) == 3:
+ p[0] = p[1] + "&"
+ else:
+ p[0] = "asso<" + p[2] + "&>"
+
+ 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_1(self, p):
+ """enum : attribute_section ENUM NAME LBRACE enum_value_list \
+ RBRACE SEMI
+ | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
+ COMMA RBRACE SEMI"""
+ p[0] = ast.Enum(p[3], p[1], p[5], filename=self.filename,
+ lineno=p.lineno(2))
+
+ def p_enum_2(self, p):
+ """enum : attribute_section ENUM NAME SEMI"""
+ p[0] = ast.Enum(p[3], p[1], None, filename=self.filename,
+ lineno=p.lineno(2))
+
+ def p_enum_value_list_1(self, p):
+ """enum_value_list : """
+ p[0] = ast.EnumValueList()
+
+ def p_enum_value_list_2(self, p):
+ """enum_value_list : nonempty_enum_value_list"""
+ p[0] = p[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 : attribute_section NAME
+ | attribute_section NAME EQUALS int
+ | attribute_section NAME EQUALS identifier_wrapped"""
+ p[0] = ast.EnumValue(p[2], p[1], p[4] if len(p) == 5 else None,
+ filename=self.filename, lineno=p.lineno(2))
+
+ 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):
+ """Parse source file to AST.
+
+ Args:
+ source: The source text as a str.
+ filename: The filename that |source| originates from.
+
+ Returns:
+ The AST as a mojom.parse.ast.Mojom object.
+ """
+ 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_tests/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py
new file mode 100644
index 0000000000..d56faadb19
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/fileutil_unittest.py
@@ -0,0 +1,55 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import imp
+import os.path
+import shutil
+import sys
+import tempfile
+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"))
+from mojom import fileutil
+
+
+class FileUtilTest(unittest.TestCase):
+
+ def testEnsureDirectoryExists(self):
+ """Test that EnsureDirectoryExists fuctions correctly."""
+
+ temp_dir = tempfile.mkdtemp()
+ try:
+ self.assertTrue(os.path.exists(temp_dir))
+
+ # Directory does not exist, yet.
+ full = os.path.join(temp_dir, "foo", "bar")
+ self.assertFalse(os.path.exists(full))
+
+ # Create the directory.
+ fileutil.EnsureDirectoryExists(full)
+ self.assertTrue(os.path.exists(full))
+
+ # Trying to create it again does not cause an error.
+ fileutil.EnsureDirectoryExists(full)
+ self.assertTrue(os.path.exists(full))
+
+ # Bypass check for directory existence to tickle error handling that
+ # occurs in response to a race.
+ fileutil.EnsureDirectoryExists(full, always_try_to_create=True)
+ self.assertTrue(os.path.exists(full))
+ finally:
+ shutil.rmtree(temp_dir)
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/__init__.py
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py
new file mode 100644
index 0000000000..70c92a38fb
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/data_unittest.py
@@ -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.
+
+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"))
+from mojom.generate import data
+from mojom.generate import module as mojom
+
+
+class DataTest(unittest.TestCase):
+
+ def testStructDataConversion(self):
+ """Tests that a struct can be converted from data."""
+ module = mojom.Module('test_module', 'test_namespace')
+ struct_data = {
+ 'name': 'SomeStruct',
+ 'enums': [],
+ 'constants': [],
+ 'fields': [
+ {'name': 'field1', 'kind': 'i32'},
+ {'name': 'field2', 'kind': 'i32', 'ordinal': 10},
+ {'name': 'field3', 'kind': 'i32', 'default': 15}]}
+
+ struct = data.StructFromData(module, struct_data)
+ struct.fields = map(lambda field:
+ data.StructFieldFromData(module, field, struct), struct.fields_data)
+ self.assertEquals(struct_data, data.StructToData(struct))
+
+ def testUnionDataConversion(self):
+ """Tests that a union can be converted from data."""
+ module = mojom.Module('test_module', 'test_namespace')
+ union_data = {
+ 'name': 'SomeUnion',
+ 'fields': [
+ {'name': 'field1', 'kind': 'i32'},
+ {'name': 'field2', 'kind': 'i32', 'ordinal': 10}]}
+
+ union = data.UnionFromData(module, union_data)
+ union.fields = map(lambda field:
+ data.UnionFieldFromData(module, field, union), union.fields_data)
+ self.assertEquals(union_data, data.UnionToData(union))
+
+ def testImportFromDataNoMissingImports(self):
+ """Tests that unions, structs, interfaces and enums are imported."""
+ module = mojom.Module('test_module', 'test_namespace')
+ imported_module = mojom.Module('import_module', 'import_namespace')
+ #TODO(azani): Init values in module.py.
+ #TODO(azani): Test that values are imported.
+ imported_module.values = {}
+ imported_data = {'module' : imported_module}
+
+
+ struct = mojom.Struct('TestStruct', module=module)
+ imported_module.kinds[struct.spec] = struct
+
+ union = mojom.Union('TestUnion', module=module)
+ imported_module.kinds[union.spec] = union
+
+ interface = mojom.Interface('TestInterface', module=module)
+ imported_module.kinds[interface.spec] = interface
+
+ enum = mojom.Enum('TestEnum', module=module)
+ imported_module.kinds[enum.spec] = enum
+
+ data.ImportFromData(module, imported_data)
+
+ # Test that the kind was imported.
+ self.assertIn(struct.spec, module.kinds)
+ self.assertEquals(struct.name, module.kinds[struct.spec].name)
+
+ self.assertIn(union.spec, module.kinds)
+ self.assertEquals(union.name, module.kinds[union.spec].name)
+
+ self.assertIn(interface.spec, module.kinds)
+ self.assertEquals(interface.name, module.kinds[interface.spec].name)
+
+ self.assertIn(enum.spec, module.kinds)
+ self.assertEquals(enum.name, module.kinds[enum.spec].name)
+
+ # Test that the imported kind is a copy and not the original.
+ self.assertIsNot(struct, module.kinds[struct.spec])
+ self.assertIsNot(union, module.kinds[union.spec])
+ self.assertIsNot(interface, module.kinds[interface.spec])
+ self.assertIsNot(enum, module.kinds[enum.spec])
+
+ def testImportFromDataNoExtraneousImports(self):
+ """Tests that arrays, maps and interface requests are not imported."""
+ module = mojom.Module('test_module', 'test_namespace')
+ imported_module = mojom.Module('import_module', 'import_namespace')
+ #TODO(azani): Init values in module.py.
+ imported_module.values = {}
+ imported_data = {'module' : imported_module}
+
+ array = mojom.Array(mojom.INT16, length=20)
+ imported_module.kinds[array.spec] = array
+
+ map_kind = mojom.Map(mojom.INT16, mojom.INT16)
+ imported_module.kinds[map_kind.spec] = map_kind
+
+ interface = mojom.Interface('TestInterface', module=module)
+ imported_module.kinds[interface.spec] = interface
+
+ interface_req = mojom.InterfaceRequest(interface)
+ imported_module.kinds[interface_req.spec] = interface_req
+
+ data.ImportFromData(module, imported_data)
+
+ self.assertNotIn(array.spec, module.kinds)
+ self.assertNotIn(map_kind.spec, module.kinds)
+ self.assertNotIn(interface_req.spec, module.kinds)
+
+ def testNonInterfaceAsInterfaceRequest(self):
+ """Tests that a non-interface cannot be used for interface requests."""
+ module = mojom.Module('test_module', 'test_namespace')
+ interface = mojom.Interface('TestInterface', module=module)
+ method_dict = {
+ 'name': 'Foo',
+ 'parameters': [{'name': 'foo', 'kind': 'r:i32'}],
+ }
+ with self.assertRaises(Exception) as e:
+ data.MethodFromData(module, method_dict, interface)
+ self.assertEquals(e.exception.__str__(),
+ 'Interface request requires \'i32\' to be an interface.')
+
+ def testNonInterfaceAsAssociatedInterface(self):
+ """Tests that a non-interface type cannot be used for associated
+ interfaces."""
+ module = mojom.Module('test_module', 'test_namespace')
+ interface = mojom.Interface('TestInterface', module=module)
+ method_dict = {
+ 'name': 'Foo',
+ 'parameters': [{'name': 'foo', 'kind': 'asso:i32'}],
+ }
+ with self.assertRaises(Exception) as e:
+ data.MethodFromData(module, method_dict, interface)
+ self.assertEquals(
+ e.exception.__str__(),
+ 'Associated interface requires \'i32\' to be an interface.')
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py
new file mode 100644
index 0000000000..a684773719
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py
@@ -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.
+
+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"))
+from mojom.generate import generator
+
+
+class StringManipulationTest(unittest.TestCase):
+ """generator contains some string utilities, this tests only those."""
+
+ def testUnderToCamel(self):
+ """Tests UnderToCamel which converts underscore_separated to CamelCase."""
+ self.assertEquals("CamelCase", generator.UnderToCamel("camel_case"))
+ self.assertEquals("CamelCase", generator.UnderToCamel("CAMEL_CASE"))
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py
new file mode 100644
index 0000000000..75b7cd5437
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/module_unittest.py
@@ -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.
+
+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"))
+from mojom.generate import module as mojom
+
+
+class ModuleTest(unittest.TestCase):
+
+ def testNonInterfaceAsInterfaceRequest(self):
+ """Tests that a non-interface cannot be used for interface requests."""
+ module = mojom.Module('test_module', 'test_namespace')
+ struct = mojom.Struct('TestStruct', module=module)
+ with self.assertRaises(Exception) as e:
+ mojom.InterfaceRequest(struct)
+ self.assertEquals(
+ e.exception.__str__(),
+ 'Interface request requires \'x:TestStruct\' to be an interface.')
+
+ def testNonInterfaceAsAssociatedInterface(self):
+ """Tests that a non-interface type cannot be used for associated interfaces.
+ """
+ module = mojom.Module('test_module', 'test_namespace')
+ struct = mojom.Struct('TestStruct', module=module)
+ with self.assertRaises(Exception) as e:
+ mojom.AssociatedInterface(struct)
+ self.assertEquals(
+ e.exception.__str__(),
+ 'Associated interface requires \'x:TestStruct\' to be an interface.')
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py
new file mode 100644
index 0000000000..75f6d5163e
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/pack_unittest.py
@@ -0,0 +1,136 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+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"))
+from mojom.generate import pack
+from mojom.generate import module as mojom
+
+
+# TODO(yzshen): Move tests in pack_tests.py here.
+class PackTest(unittest.TestCase):
+ def _CheckPackSequence(self, kinds, fields, offsets):
+ """Checks the pack order and offsets of a sequence of mojom.Kinds.
+
+ Args:
+ kinds: A sequence of mojom.Kinds that specify the fields that are to be
+ created.
+ fields: The expected order of the resulting fields, with the integer "1"
+ first.
+ offsets: The expected order of offsets, with the integer "0" first.
+ """
+ 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)
+ self.assertEquals(len(kinds), num_fields)
+ for i in xrange(num_fields):
+ self.assertEquals('%d' % fields[i], ps.packed_fields[i].field.name)
+ self.assertEquals(offsets[i], ps.packed_fields[i].offset)
+
+ def testMinVersion(self):
+ """Tests that |min_version| is properly set for packed fields."""
+ struct = mojom.Struct('test')
+ struct.AddField('field_2', mojom.BOOL, 2)
+ struct.AddField('field_0', mojom.INT32, 0)
+ struct.AddField('field_1', mojom.INT64, 1)
+ ps = pack.PackedStruct(struct)
+
+ self.assertEquals('field_0', ps.packed_fields[0].field.name)
+ self.assertEquals('field_2', ps.packed_fields[1].field.name)
+ self.assertEquals('field_1', ps.packed_fields[2].field.name)
+
+ self.assertEquals(0, ps.packed_fields[0].min_version)
+ self.assertEquals(0, ps.packed_fields[1].min_version)
+ self.assertEquals(0, ps.packed_fields[2].min_version)
+
+ struct.fields[0].attributes = {'MinVersion': 1}
+ ps = pack.PackedStruct(struct)
+
+ self.assertEquals(0, ps.packed_fields[0].min_version)
+ self.assertEquals(1, ps.packed_fields[1].min_version)
+ self.assertEquals(0, ps.packed_fields[2].min_version)
+
+ def testGetVersionInfoEmptyStruct(self):
+ """Tests that pack.GetVersionInfo() never returns an empty list, even for
+ empty structs.
+ """
+ struct = mojom.Struct('test')
+ ps = pack.PackedStruct(struct)
+
+ versions = pack.GetVersionInfo(ps)
+ self.assertEquals(1, len(versions))
+ self.assertEquals(0, versions[0].version)
+ self.assertEquals(0, versions[0].num_fields)
+ self.assertEquals(8, versions[0].num_bytes)
+
+ def testGetVersionInfoComplexOrder(self):
+ """Tests pack.GetVersionInfo() using a struct whose definition order,
+ ordinal order and pack order for fields are all different.
+ """
+ struct = mojom.Struct('test')
+ struct.AddField('field_3', mojom.BOOL, ordinal=3,
+ attributes={'MinVersion': 3})
+ struct.AddField('field_0', mojom.INT32, ordinal=0)
+ struct.AddField('field_1', mojom.INT64, ordinal=1,
+ attributes={'MinVersion': 2})
+ struct.AddField('field_2', mojom.INT64, ordinal=2,
+ attributes={'MinVersion': 3})
+ ps = pack.PackedStruct(struct)
+
+ versions = pack.GetVersionInfo(ps)
+ self.assertEquals(3, len(versions))
+
+ self.assertEquals(0, versions[0].version)
+ self.assertEquals(1, versions[0].num_fields)
+ self.assertEquals(16, versions[0].num_bytes)
+
+ self.assertEquals(2, versions[1].version)
+ self.assertEquals(2, versions[1].num_fields)
+ self.assertEquals(24, versions[1].num_bytes)
+
+ self.assertEquals(3, versions[2].version)
+ self.assertEquals(4, versions[2].num_fields)
+ self.assertEquals(32, versions[2].num_bytes)
+
+ def testInterfaceAlignment(self):
+ """Tests that interfaces are aligned on 4-byte boundaries, although the size
+ of an interface is 8 bytes.
+ """
+ kinds = (mojom.INT32, mojom.Interface('test_interface'))
+ fields = (1, 2)
+ offsets = (0, 4)
+ self._CheckPackSequence(kinds, fields, offsets)
+
+ def testAssociatedInterfaceAlignment(self):
+ """Tests that associated interfaces are aligned on 4-byte boundaries,
+ although the size of an associated interface is 8 bytes.
+ """
+ kinds = (mojom.INT32,
+ mojom.AssociatedInterface(mojom.Interface('test_interface')))
+ fields = (1, 2)
+ offsets = (0, 4)
+ self._CheckPackSequence(kinds, fields, offsets)
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 0000000000..e69de29bb2
--- /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 0000000000..dd28cdd120
--- /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 0000000000..6822cbc8d0
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
@@ -0,0 +1,192 @@
+# Copyright 2014 The Chromium 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("union"),
+ _MakeLexTokenForKeyword("union"))
+ 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"))
+ self.assertEquals(self._SingleTokenForInput("map"),
+ _MakeLexTokenForKeyword("map"))
+ self.assertEquals(self._SingleTokenForInput("associated"),
+ _MakeLexTokenForKeyword("associated"))
+
+ 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 0000000000..3f4ca871e3
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
@@ -0,0 +1,1497 @@
+# Copyright 2014 The Chromium 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"
+ 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;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, None, 'int32', None),
+ ast.StructField('b', None, None, 'double', None)]))])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testSimpleStructWithoutModule(self):
+ """Tests a simple struct without an explict module statement."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ double b;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, None, 'int32', None),
+ ast.StructField('b', None, 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',
+ None,
+ ast.EnumValueList(
+ ast.EnumValue('VALUE', None, None))),
+ ast.Const('kMyConst', 'double', '1.23'),
+ ast.StructField('a', None, None, 'int32', None),
+ ast.StructField('b', None, 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.
+
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testMultipleModuleStatements(self):
+ """Tests an (invalid) .mojom with multiple module statements."""
+
+ source = """\
+ module foo;
+ module bar;
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Multiple \"module\" statements not "
+ r"allowed:\n *module bar;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testModuleStatementAfterImport(self):
+ """Tests an (invalid) .mojom with a module statement after an import."""
+
+ source = """\
+ import "foo.mojom";
+ module foo;
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: \"module\" statements must precede imports "
+ r"and definitions:\n *module foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testModuleStatementAfterDefinition(self):
+ """Tests an (invalid) .mojom with a module statement after a definition."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ };
+ module foo;
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: \"module\" statements must precede imports "
+ r"and definitions:\n *module foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testImportStatementAfterDefinition(self):
+ """Tests an (invalid) .mojom with an import statement after a definition."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ };
+ import "foo.mojom";
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: \"import\" statements must precede "
+ r"definitions:\n *import \"foo.mojom\";$"):
+ parser.Parse(source, "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.
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Enum(
+ 'MyEnum1',
+ None,
+ ast.EnumValueList([ast.EnumValue('VALUE1', None, None),
+ ast.EnumValue('VALUE2', None, None)])),
+ ast.Enum(
+ 'MyEnum2',
+ None,
+ ast.EnumValueList([ast.EnumValue('VALUE1', None, '-1'),
+ ast.EnumValue('VALUE2', None, '0'),
+ ast.EnumValue('VALUE3', None, '+987'),
+ ast.EnumValue('VALUE4', None, '0xAF12'),
+ ast.EnumValue('VALUE5', None, '-0x09bcd'),
+ ast.EnumValue('VALUE6', None, ('IDENTIFIER',
+ 'VALUE5')),
+ ast.EnumValue('VALUE7', None, 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;
+ };
+ """
+ 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', None, 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
+ };
+ """
+ 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;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a0', None, ast.Ordinal(0), 'int32', None),
+ ast.StructField('a1', None, ast.Ordinal(1), 'int32', None),
+ ast.StructField('a2', None, ast.Ordinal(2), 'int32', None),
+ ast.StructField('a9', None, ast.Ordinal(9), 'int32', None),
+ ast.StructField('a10', None, ast.Ordinal(10), 'int32', None),
+ ast.StructField('a11', None, ast.Ordinal(11), 'int32', None),
+ ast.StructField('a29', None, ast.Ordinal(29), 'int32', None),
+ ast.StructField('a1234567890', None, 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@;
+ };
+ """
+ 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;
+ };
+ """
+ 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;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my.mod'), None),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(ast.StructField('a', None, 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, None, 'handle', None),
+ ast.StructField('b', None, None, 'handle<data_pipe_consumer>',
+ None),
+ ast.StructField('c', None, None, 'handle<data_pipe_producer>',
+ None),
+ ast.StructField('d', None, None, 'handle<message_pipe>', None),
+ ast.StructField('e', None, 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, None, 'int16', '0'),
+ ast.StructField('a1', None, None, 'uint16', '0x0'),
+ ast.StructField('a2', None, None, 'uint16', '0x00'),
+ ast.StructField('a3', None, None, 'uint16', '0x01'),
+ ast.StructField('a4', None, None, 'uint16', '0xcd'),
+ ast.StructField('a5' , None, None, 'int32', '12345'),
+ ast.StructField('a6', None, None, 'int64', '-12345'),
+ ast.StructField('a7', None, None, 'int64', '+12345'),
+ ast.StructField('a8', None, None, 'uint32', '0x12cd3'),
+ ast.StructField('a9', None, None, 'uint32', '-0x12cD3'),
+ ast.StructField('a10', None, None, 'uint32', '+0x12CD3'),
+ ast.StructField('a11', None, None, 'bool', 'true'),
+ ast.StructField('a12', None, None, 'bool', 'false'),
+ ast.StructField('a13', None, None, 'float', '1.2345'),
+ ast.StructField('a14', None, None, 'float', '-1.2345'),
+ ast.StructField('a15', None, None, 'float', '+1.2345'),
+ ast.StructField('a16', None, None, 'float', '123.'),
+ ast.StructField('a17', None, None, 'float', '.123'),
+ ast.StructField('a18', None, None, 'double', '1.23E10'),
+ ast.StructField('a19', None, None, 'double', '1.E-10'),
+ ast.StructField('a20', None, None, 'double', '.5E+10'),
+ ast.StructField('a21', None, None, 'double', '-1.23E10'),
+ ast.StructField('a22', None, 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;
+ array<array<array<int32, 1>>, 2> nested_arrays;
+ };
+ """
+ expected = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('normal_array', None, None, 'int32[]', None),
+ ast.StructField('fixed_size_array_one_entry', None, None,
+ 'int32[1]', None),
+ ast.StructField('fixed_size_array_ten_entries', None, None,
+ 'int32[10]', None),
+ ast.StructField('nested_arrays', None, None,
+ 'int32[1][][2]', 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, 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 testValidAssociativeArrays(self):
+ """Tests that we can parse valid associative array structures."""
+
+ source1 = "struct MyStruct { map<string, uint8> data; };"
+ expected1 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('data', None, None, 'uint8{string}', None)]))])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ source2 = "interface MyInterface { MyMethod(map<string, uint8> a); };"
+ expected2 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Interface(
+ 'MyInterface',
+ None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod',
+ None,
+ None,
+ ast.ParameterList(
+ ast.Parameter('a', None, None, 'uint8{string}')),
+ None)))])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ source3 = "struct MyStruct { map<string, array<uint8>> data; };"
+ expected3 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('data', None, None, 'uint8[]{string}',
+ None)]))])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ 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,
+ None,
+ ast.ParameterList(ast.Parameter('a', None, 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',
+ None,
+ ast.Ordinal(0),
+ ast.ParameterList([ast.Parameter('a', None, ast.Ordinal(0),
+ 'int32'),
+ ast.Parameter('b', None, ast.Ordinal(1),
+ 'int64')]),
+ None),
+ ast.Method(
+ 'MyMethod2',
+ None,
+ 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,
+ None,
+ ast.ParameterList(ast.Parameter('a', None, None, 'string')),
+ ast.ParameterList([ast.Parameter('a', None, None, 'int32'),
+ ast.Parameter('b', None, 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',
+ None,
+ ast.EnumValueList(
+ ast.EnumValue('VALUE', None, None))),
+ ast.Const('kMyConst', 'int32', '123'),
+ ast.Method(
+ 'MyMethod',
+ None,
+ None,
+ ast.ParameterList(ast.Parameter('x', None, None, 'int32')),
+ ast.ParameterList(ast.Parameter('y', None, 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)
+
+ # Various places that attribute list is allowed.
+ source4 = """\
+ [Attr0=0] module my_module;
+
+ [Attr1=1] struct MyStruct {
+ [Attr2=2] int32 a;
+ };
+ [Attr3=3] union MyUnion {
+ [Attr4=4] int32 a;
+ };
+ [Attr5=5] enum MyEnum {
+ [Attr6=6] a
+ };
+ [Attr7=7] interface MyInterface {
+ [Attr8=8] MyMethod([Attr9=9] int32 a) => ([Attr10=10] bool b);
+ };
+ """
+ expected4 = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'),
+ ast.AttributeList([ast.Attribute("Attr0", 0)])),
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ ast.AttributeList(ast.Attribute("Attr1", 1)),
+ ast.StructBody(
+ ast.StructField(
+ 'a', ast.AttributeList([ast.Attribute("Attr2", 2)]),
+ None, 'int32', None))),
+ ast.Union(
+ 'MyUnion',
+ ast.AttributeList(ast.Attribute("Attr3", 3)),
+ ast.UnionBody(
+ ast.UnionField(
+ 'a', ast.AttributeList([ast.Attribute("Attr4", 4)]), None,
+ 'int32'))),
+ ast.Enum(
+ 'MyEnum',
+ ast.AttributeList(ast.Attribute("Attr5", 5)),
+ ast.EnumValueList(
+ ast.EnumValue(
+ 'VALUE', ast.AttributeList([ast.Attribute("Attr6", 6)]),
+ None))),
+ ast.Interface(
+ 'MyInterface',
+ ast.AttributeList(ast.Attribute("Attr7", 7)),
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod',
+ ast.AttributeList(ast.Attribute("Attr8", 8)),
+ None,
+ ast.ParameterList(
+ ast.Parameter(
+ 'a', ast.AttributeList([ast.Attribute("Attr9", 9)]),
+ None, 'int32')),
+ ast.ParameterList(
+ ast.Parameter(
+ 'b',
+ ast.AttributeList([ast.Attribute("Attr10", 10)]),
+ None, 'bool')))))])
+ self.assertEquals(parser.Parse(source4, "my_file.mojom"), expected4)
+
+ # 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 = """\
+ module my_module;
+ import "somedir/my1.mojom";
+ import "somedir/my2.mojom";
+ """
+ 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.
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ import "foo.mojom" // Missing semicolon.
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source3, "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, None,'int32?', None),
+ ast.StructField('b', None, None,'string?', None),
+ ast.StructField('c', None, None,'int32[]?', None),
+ ast.StructField('d', None, None,'string?[]?', None),
+ ast.StructField('e', None, None,'int32[]?[]?', None),
+ ast.StructField('f', None, None,'int32[1]?', None),
+ ast.StructField('g', None, None,'string?[1]?', None),
+ ast.StructField('h', None, None,'some_struct?', None),
+ ast.StructField('i', None, None,'handle?', None),
+ ast.StructField('j', None, None,'handle<data_pipe_consumer>?',
+ None),
+ ast.StructField('k', None, None,'handle<data_pipe_producer>?',
+ None),
+ ast.StructField('l', None, None,'handle<message_pipe>?', None),
+ ast.StructField('m', None, None,'handle<shared_buffer>?',
+ None),
+ ast.StructField('n', None, 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")
+
+ def testSimpleUnion(self):
+ """Tests a simple .mojom source that just defines a union."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ int32 a;
+ double b;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Union(
+ 'MyUnion',
+ None,
+ ast.UnionBody([
+ ast.UnionField('a', None, None, 'int32'),
+ ast.UnionField('b', None, None, 'double')
+ ]))])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithOrdinals(self):
+ """Test that ordinals are assigned to fields."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ int32 a @10;
+ double b @30;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Union(
+ 'MyUnion',
+ None,
+ ast.UnionBody([
+ ast.UnionField('a', None, ast.Ordinal(10), 'int32'),
+ ast.UnionField('b', None, ast.Ordinal(30), 'double')
+ ]))])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithStructMembers(self):
+ """Test that struct members are accepted."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ SomeStruct s;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Union(
+ 'MyUnion',
+ None,
+ ast.UnionBody([
+ ast.UnionField('s', None, None, 'SomeStruct')
+ ]))])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithArrayMember(self):
+ """Test that array members are accepted."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ array<int32> a;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Union(
+ 'MyUnion',
+ None,
+ ast.UnionBody([
+ ast.UnionField('a', None, None, 'int32[]')
+ ]))])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithMapMember(self):
+ """Test that map members are accepted."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ map<int32, string> m;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList(),
+ [ast.Union(
+ 'MyUnion',
+ None,
+ ast.UnionBody([
+ ast.UnionField('m', None, None, 'string{int32}')
+ ]))])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionDisallowNestedStruct(self):
+ """Tests that structs cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ struct MyStruct {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testUnionDisallowNestedInterfaces(self):
+ """Tests that interfaces cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ interface MyInterface {
+ MyMethod(int32 a);
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'interface':\n"
+ r" *interface MyInterface {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testUnionDisallowNestedUnion(self):
+ """Tests that unions cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ union MyOtherUnion {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'union':\n"
+ r" *union MyOtherUnion {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testUnionDisallowNestedEnum(self):
+ """Tests that enums cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ enum MyEnum {
+ A,
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'enum':\n"
+ r" *enum MyEnum {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testValidAssociatedKinds(self):
+ """Tests parsing associated interfaces and requests."""
+ source1 = """\
+ struct MyStruct {
+ associated MyInterface a;
+ associated MyInterface& b;
+ associated MyInterface? c;
+ associated MyInterface&? d;
+ };
+ """
+ expected1 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Struct(
+ 'MyStruct',
+ None,
+ ast.StructBody(
+ [ast.StructField('a', None, None,'asso<MyInterface>', None),
+ ast.StructField('b', None, None,'asso<MyInterface&>', None),
+ ast.StructField('c', None, None,'asso<MyInterface>?', None),
+ ast.StructField('d', None, None,'asso<MyInterface&>?',
+ None)]))])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ source2 = """\
+ interface MyInterface {
+ MyMethod(associated A a) =>(associated B& b);
+ };"""
+ expected2 = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Interface(
+ 'MyInterface',
+ None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod',
+ None,
+ None,
+ ast.ParameterList(
+ ast.Parameter('a', None, None, 'asso<A>')),
+ ast.ParameterList(
+ ast.Parameter('b', None, None, 'asso<B&>')))))])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ def testInvalidAssociatedKinds(self):
+ """Tests that invalid associated interfaces and requests are correctly
+ detected."""
+ source1 = """\
+ struct MyStruct {
+ associated associated SomeInterface a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'associated':\n"
+ r" *associated associated SomeInterface a;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ associated handle a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'handle':\n"
+ r" *associated handle a;$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ associated? MyInterface& a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '\?':\n"
+ r" *associated\? MyInterface& 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 0000000000..b160de6690
--- /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 0000000000..899d40e584
--- /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/parse/translate_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py
new file mode 100644
index 0000000000..25203329f4
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py
@@ -0,0 +1,80 @@
+# Copyright 2014 The Chromium 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"))
+from mojom.parse import ast
+from mojom.parse import translate
+
+
+class TranslateTest(unittest.TestCase):
+ """Tests |parser.Parse()|."""
+
+ def testSimpleArray(self):
+ """Tests a simple int32[]."""
+ # pylint: disable=W0212
+ self.assertEquals(translate._MapKind("int32[]"), "a:i32")
+
+ def testAssociativeArray(self):
+ """Tests a simple uint8{string}."""
+ # pylint: disable=W0212
+ self.assertEquals(translate._MapKind("uint8{string}"), "m[s][u8]")
+
+ def testLeftToRightAssociativeArray(self):
+ """Makes sure that parsing is done from right to left on the internal kinds
+ in the presence of an associative array."""
+ # pylint: disable=W0212
+ self.assertEquals(translate._MapKind("uint8[]{string}"), "m[s][a:u8]")
+
+ def testTranslateSimpleUnions(self):
+ """Makes sure that a simple union is translated correctly."""
+ tree = ast.Mojom(
+ None,
+ ast.ImportList(),
+ [ast.Union("SomeUnion", None, ast.UnionBody(
+ [ast.UnionField("a", None, None, "int32"),
+ ast.UnionField("b", None, None, "string")]))])
+ expected = [{
+ "name": "SomeUnion",
+ "fields": [{"kind": "i32", "name": "a"},
+ {"kind": "s", "name": "b"}]}]
+ actual = translate.Translate(tree, "mojom_tree")
+ self.assertEquals(actual["unions"], expected)
+
+ def testMapTreeForTypeRaisesWithDuplicate(self):
+ """Verifies _MapTreeForType() raises when passed two values with the same
+ name."""
+ methods = [ast.Method('dup', None, None, ast.ParameterList(), None),
+ ast.Method('dup', None, None, ast.ParameterList(), None)]
+ self.assertRaises(Exception, translate._MapTreeForType,
+ (lambda x: x, methods, '', 'scope'))
+
+ def testAssociatedKinds(self):
+ """Tests type spec translation of associated interfaces and requests."""
+ # pylint: disable=W0212
+ self.assertEquals(translate._MapKind("asso<SomeInterface>?"),
+ "?asso:x:SomeInterface")
+ self.assertEquals(translate._MapKind("asso<SomeInterface&>?"),
+ "?asso:r:x:SomeInterface")
+
+
+if __name__ == "__main__":
+ unittest.main()
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 0000000000..e69de29bb2
--- /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 0000000000..2a4b17b29b
--- /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 0000000000..20ef461969
--- /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))