From e1543ddc1e3cfeb3019cdda3357240727975d9d5 Mon Sep 17 00:00:00 2001
From: rockot <rockot@chromium.org>
Date: Tue, 15 Dec 2015 21:36:09 -0800
Subject: [mojo] Add mojom parser support for native-only structs

This allows struct declarations to be parsed in mojom with no
struct body. Such structs are tagged as native-only and this
information is propagated to different language generators.

Part of a series of changes to support custom mojom serialization:

  1. This CL
  2. https://codereview.chromium.org/1517043004
  3. https://codereview.chromium.org/1524693002
  4. https://codereview.chromium.org/1520153002
  5. https://codereview.chromium.org/1524613002
  6. https://codereview.chromium.org/1526533002
  7. https://codereview.chromium.org/1524703002

BUG=569669

Review URL: https://codereview.chromium.org/1515423002

Cr-Commit-Position: refs/heads/master@{#365467}
---
 mojo/public/interfaces/bindings/tests/BUILD.gn     |  1 +
 .../bindings/tests/test_native_types.mojom         | 11 +++++++++
 .../tools/bindings/pylib/mojom/generate/data.py    | 26 +++++++++++++++++-----
 .../tools/bindings/pylib/mojom/generate/module.py  |  2 ++
 .../public/tools/bindings/pylib/mojom/parse/ast.py |  2 +-
 .../tools/bindings/pylib/mojom/parse/parser.py     |  6 ++++-
 .../tools/bindings/pylib/mojom/parse/translate.py  | 15 ++++++++-----
 7 files changed, 49 insertions(+), 14 deletions(-)
 create mode 100644 mojo/public/interfaces/bindings/tests/test_native_types.mojom

diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
index d5baa9d..802bd58 100644
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -20,6 +20,7 @@ mojom("test_interfaces") {
     "scoping.mojom",
     "serialization_test_structs.mojom",
     "test_constants.mojom",
+    "test_native_types.mojom",
     "test_structs.mojom",
     "validation_test_interfaces.mojom",
   ]
diff --git a/mojo/public/interfaces/bindings/tests/test_native_types.mojom b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
new file mode 100644
index 0000000..3537703
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
@@ -0,0 +1,11 @@
+// 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.
+
+module mojo.test;
+
+// Used to verify that structs can be declared with no body in mojom.
+
+[native=True]
+struct PickledStruct;
+
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
index 1109a6a..60ae1e0 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/data.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -209,15 +209,29 @@ def StructToData(struct):
 def StructFromData(module, data):
   struct = mojom.Struct(module=module)
   struct.name = data['name']
+  struct.native_only = data['native_only']
   struct.spec = 'x:' + module.namespace + '.' + struct.name
   module.kinds[struct.spec] = struct
-  struct.enums = map(lambda enum:
-      EnumFromData(module, enum, struct), data['enums'])
-  struct.constants = map(lambda constant:
-      ConstantFromData(module, constant, struct), data['constants'])
-  # Stash fields data here temporarily.
-  struct.fields_data = data['fields']
+  if struct.native_only:
+    struct.enums = []
+    struct.constants = []
+    struct.fields_data = []
+  else:
+    struct.enums = map(lambda enum:
+        EnumFromData(module, enum, struct), data['enums'])
+    struct.constants = map(lambda constant:
+        ConstantFromData(module, constant, struct), data['constants'])
+    # Stash fields data here temporarily.
+    struct.fields_data = data['fields']
   struct.attributes = data.get('attributes')
+
+  # Enforce that a [native=True] 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=True attribute.")
+
   return struct
 
 def UnionToData(union):
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
index 8af874f..dfde609 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -202,6 +202,7 @@ class UnionField(Field): pass
 
 class Struct(ReferenceKind):
   ReferenceKind.AddSharedProperty('name')
+  ReferenceKind.AddSharedProperty('native_only')
   ReferenceKind.AddSharedProperty('module')
   ReferenceKind.AddSharedProperty('imported_from')
   ReferenceKind.AddSharedProperty('fields')
@@ -214,6 +215,7 @@ class Struct(ReferenceKind):
       spec = None
     ReferenceKind.__init__(self, spec)
     self.name = name
+    self.native_only = False
     self.module = module
     self.imported_from = None
     self.fields = []
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
index ffb44e0..6473b02 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -326,7 +326,7 @@ class Struct(Definition):
 
   def __init__(self, name, attribute_list, body, **kwargs):
     assert attribute_list is None or isinstance(attribute_list, AttributeList)
-    assert isinstance(body, StructBody)
+    assert isinstance(body, StructBody) or body is None
     super(Struct, self).__init__(name, **kwargs)
     self.attribute_list = attribute_list
     self.body = body
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
index f52f91d..8d90cd5 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -155,10 +155,14 @@ class Parser(object):
     # 'eval' the literal to strip the quotes.
     p[0] = eval(p[1])
 
-  def p_struct(self, p):
+  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()
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
index aad245f..e457f99 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
@@ -130,12 +130,15 @@ class _MojomBuilder(object):
 
       assert isinstance(struct, ast.Struct)
       data = {'name': struct.name,
-              'fields': _MapTreeForType(StructFieldToDict, struct.body,
-                                        ast.StructField, struct.name),
-              'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum,
-                                       struct.name),
-              'constants': _MapTreeForType(_ConstToDict, struct.body,
-                                           ast.Const, struct.name)}
+              'native_only': struct.body is None}
+      if not data['native_only']:
+        data.update({
+            'fields': _MapTreeForType(StructFieldToDict, struct.body,
+                                      ast.StructField, struct.name),
+            'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum,
+                                     struct.name),
+            'constants': _MapTreeForType(_ConstToDict, struct.body,
+                                         ast.Const, struct.name)})
       _AddOptional(data, 'attributes',
                    _AttributeListToDict(struct.attribute_list))
       return data
-- 
cgit v1.1