Mojo code generator: change where to add computed data to mojom definitions

So that the same work is not done multiple times.

BUG=718614

Review-Url: https://codereview.chromium.org/2863353002
Cr-Commit-Position: refs/heads/master@{#470986}


CrOS-Libchrome-Original-Commit: 121205ac1fd5cf2274a6f429dec741fb602f7081
diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
index 85d95d6..afee2ba 100644
--- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -204,7 +204,7 @@
   }
 
   var {{interface.name}} = {
-    name: '{{namespace|replace(".","::")}}::{{interface.name}}',
+    name: '{{module.namespace|replace(".","::")}}::{{interface.name}}',
     kVersion: {{interface.version}},
     ptrClass: {{interface.name}}Ptr,
     proxyClass: {{interface.name}}Proxy,
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index d2930d1..7b55afc 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -278,12 +278,10 @@
           yield param.kind
 
   def _GetJinjaExports(self):
-    structs = self.GetStructs()
-    interfaces = self.GetInterfaces()
     all_enums = list(self.module.enums)
-    for struct in structs:
+    for struct in self.module.structs:
       all_enums.extend(struct.enums)
-    for interface in interfaces:
+    for interface in self.module.interfaces:
       all_enums.extend(interface.enums)
 
     return {
@@ -294,9 +292,9 @@
       "kinds": self.module.kinds,
       "enums": self.module.enums,
       "all_enums": all_enums,
-      "structs": structs,
-      "unions": self.GetUnions(),
-      "interfaces": interfaces,
+      "structs": self.module.structs,
+      "unions": self.module.unions,
+      "interfaces": self.module.interfaces,
       "variant": self.variant,
       "extra_traits_headers": self._GetExtraTraitsHeaders(),
       "extra_public_headers": self._GetExtraPublicHeaders(),
@@ -356,7 +354,7 @@
       "is_union_kind": mojom.IsUnionKind,
       "passes_associated_kinds": mojom.PassesAssociatedKinds,
       "struct_constructors": self._GetStructConstructors,
-      "under_to_camel": generator.UnderToCamel,
+      "under_to_camel": generator.ToCamel,
       "unmapped_type_for_serializer": self._GetUnmappedTypeForSerializer,
       "wtf_hash_fn_name_for_enum": GetWtfHashFnNameForEnum,
     }
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py
index 33c47c8..3e6f63a 100644
--- a/mojo/public/tools/bindings/generators/mojom_java_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -486,9 +486,7 @@
   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():
+    for struct in self.module.structs:
       self.Write(self._GenerateStructSource(struct),
                  '%s.java' % GetNameForElement(struct))
 
@@ -500,7 +498,7 @@
       self.Write(self._GenerateEnumSource(enum),
                  '%s.java' % GetNameForElement(enum))
 
-    for interface in self.GetInterfaces():
+    for interface in self.module.interfaces:
       self.Write(self._GenerateInterfaceSource(interface),
                  '%s.java' % GetNameForElement(interface))
       self.Write(self._GenerateInterfaceInternalSource(interface),
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
index 20a2b23..ef4c2ef 100644
--- a/mojo/public/tools/bindings/generators/mojom_js_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -95,16 +95,14 @@
 class Generator(generator.Generator):
   def _GetParameters(self):
     return {
-      "namespace": self.module.namespace,
-      "imports": self._GetImports(),
-      "kinds": self.module.kinds,
       "enums": self.module.enums,
+      "imports": self.module.imports,
+      "interfaces": self.module.interfaces,
+      "kinds": self.module.kinds,
       "module": self.module,
-      "structs": self.GetStructs() + self.GetStructsFromMethods(),
-      "unions": self.GetUnions(),
+      "structs": self.module.structs + self._GetStructsFromMethods(),
+      "unions": self.module.unions,
       "use_new_js_bindings": self.use_new_js_bindings,
-      "interfaces": self.GetInterfaces(),
-      "imported_interfaces": self._GetImportedInterfaces(),
     }
 
   @staticmethod
@@ -118,6 +116,7 @@
       "encode_snippet": self._JavaScriptEncodeSnippet,
       "expression_to_text": self._ExpressionToText,
       "field_offset": JavaScriptFieldOffset,
+      "get_relative_path": GetRelativePath,
       "has_callbacks": mojom.HasCallbacks,
       "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind,
       "is_array_kind": mojom.IsArrayKind,
@@ -137,8 +136,8 @@
       "js_type": self._JavaScriptType,
       "method_passes_associated_kinds": mojom.MethodPassesAssociatedKinds,
       "payload_size": JavaScriptPayloadSize,
-      "get_relative_path": GetRelativePath,
-      "stylize_method": generator.StudlyCapsToCamel,
+      "stylize_method": lambda x: generator.ToCamel(x, lower_initial=True),
+      "to_camel": generator.ToCamel,
       "union_decode_snippet": self._JavaScriptUnionDecodeSnippet,
       "union_encode_snippet": self._JavaScriptUnionEncodeSnippet,
       "validate_array_params": self._JavaScriptValidateArrayParams,
@@ -158,11 +157,13 @@
     if self.variant:
       raise Exception("Variants not supported in JavaScript bindings.")
 
+    # TODO(yzshen): Remove this method once the old JS bindings go away.
+    self._SetUniqueNameForImports()
+
     self.Write(self._GenerateAMDModule(),
         self.MatchMojomFilePath("%s.js" % self.module.name))
 
-  def _GetImports(self):
-    # TODO(yzshen): Remove this method once the old JS bindings go away.
+  def _SetUniqueNameForImports(self):
     used_names = set()
     for each_import in self.module.imports:
       simple_name = each_import.name.split(".")[0]
@@ -178,15 +179,6 @@
       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.interfaces:
-        name = each_interface.name
-        interface_to_import[name] = each_import.unique_name + "." + name
-    return interface_to_import;
 
   def _JavaScriptType(self, kind):
     name = []
@@ -381,3 +373,11 @@
   def _ExpressionToText(self, value):
     return self._TranslateConstants(value)
 
+  def _GetStructsFromMethods(self):
+    result = []
+    for interface in self.module.interfaces:
+      for method in interface.methods:
+        result.append(method.param_struct)
+        if method.response_param_struct is not None:
+          result.append(method.response_param_struct)
+    return result
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
index 24fe610..c6f90a3 100755
--- a/mojo/public/tools/bindings/mojom_bindings_generator.py
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -41,6 +41,7 @@
 import mojom.fileutil as fileutil
 from mojom.generate import translate
 from mojom.generate import template_expander
+from mojom.generate.generator import AddComputedData
 from mojom.parse.parser import Parse
 
 
@@ -198,6 +199,7 @@
     module.path = module.path.replace('\\', '/')
 
     if self._should_generate(rel_filename.path):
+      AddComputedData(module)
       for language, generator_module in generator_modules.iteritems():
         generator = generator_module.Generator(
             module, args.output_dir, typemap=self._typemap.get(language, {}),
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
index 0e64af7..04850d7 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -12,17 +12,25 @@
 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 ToCamel(identifier, lower_initial=False, dilimiter='_'):
+  """Splits |identifier| using |dilimiter|, makes the first character of each
+  word uppercased (but makes the first character of the first word lowercased
+  if |lower_initial| is set to True), and joins the words. Please note that for
+  each word, all the characters except the first one are untouched.
+  """
+  result = ''.join(
+      word[0].upper() + word[1:] for word in identifier.split(dilimiter))
+  if lower_initial:
+    result = result[0].lower() + result[1:]
+  return result
+
 
 def WriteFile(contents, full_path):
   # Make sure the containing directory exists.
@@ -33,6 +41,76 @@
   with open(full_path, "w+") as f:
     f.write(contents)
 
+
+def AddComputedData(module):
+  """Adds computed data to the given module. The data is computed once and
+  used repeatedly in the generation process."""
+
+  def _AddStructComputedData(exported, struct):
+    struct.packed = pack.PackedStruct(struct)
+    struct.bytes = pack.GetByteLayout(struct.packed)
+    struct.versions = pack.GetVersionInfo(struct.packed)
+    struct.exported = exported
+
+  def _AddUnionComputedData(union):
+    ordinal = 0
+    for field in union.fields:
+      if field.ordinal is not None:
+        ordinal = field.ordinal
+      field.ordinal = ordinal
+      ordinal += 1
+
+  def _AddInterfaceComputedData(interface):
+    next_ordinal = 0
+    interface.version = 0
+    for method in interface.methods:
+      if method.ordinal is None:
+        method.ordinal = next_ordinal
+      next_ordinal = method.ordinal + 1
+
+      if method.min_version is not None:
+        interface.version = max(interface.version, method.min_version)
+
+      method.param_struct = _GetStructFromMethod(method)
+      interface.version = max(interface.version,
+                              method.param_struct.versions[-1].version)
+
+      if method.response_parameters is not None:
+        method.response_param_struct = _GetResponseStructFromMethod(method)
+        interface.version = max(
+            interface.version,
+            method.response_param_struct.versions[-1].version)
+      else:
+        method.response_param_struct = None
+
+  def _GetStructFromMethod(method):
+    """Converts a method's parameters into the fields of a struct."""
+    params_class = "%s_%s_Params" % (method.interface.name, method.name)
+    struct = mojom.Struct(params_class, module=method.interface.module)
+    for param in method.parameters:
+      struct.AddField(param.name, param.kind, param.ordinal,
+                      attributes=param.attributes)
+    _AddStructComputedData(False, struct)
+    return struct
+
+  def _GetResponseStructFromMethod(method):
+    """Converts a method's response_parameters into the fields of a struct."""
+    params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
+    struct = mojom.Struct(params_class, module=method.interface.module)
+    for param in method.response_parameters:
+      struct.AddField(param.name, param.kind, param.ordinal,
+                      attributes=param.attributes)
+    _AddStructComputedData(False, struct)
+    return struct
+
+  for struct in module.structs:
+    _AddStructComputedData(True, struct)
+  for union in module.unions:
+    _AddUnionComputedData(union)
+  for interface in module.interfaces:
+    _AddInterfaceComputedData(interface)
+
+
 class Generator(object):
   # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
   # files to stdout.
@@ -52,24 +130,6 @@
     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):
@@ -93,61 +153,3 @@
     """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
deleted file mode 100644
index 9966b0b..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator_unittest.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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
index fd346e2..906d3fd 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -623,9 +623,8 @@
 
 
 class Module(object):
-  def __init__(self, name=None, namespace=None, attributes=None):
-    self.name = name
-    self.path = name
+  def __init__(self, path=None, namespace=None, attributes=None):
+    self.path = path
     self.namespace = namespace
     self.structs = []
     self.unions = []
@@ -639,10 +638,10 @@
 
   def Repr(self, as_ref=True):
     if as_ref:
-      return '<%s name=%r namespace=%r>' % (
-          self.__class__.__name__, self.name, self.namespace)
+      return '<%s path=%r namespace=%r>' % (
+          self.__class__.__name__, self.path, self.namespace)
     else:
-      return GenericRepr(self, {'name': False, 'namespace': False,
+      return GenericRepr(self, {'path': False, 'namespace': False,
                                 'attributes': False, 'structs': False,
                                 'interfaces': False, 'unions': False})
 
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
index bd5f326..5e4183a 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
@@ -608,10 +608,4 @@
     {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_tests/generate/generator_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py
index a684773..e037c96 100644
--- a/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/generate/generator_unittest.py
@@ -27,10 +27,14 @@
 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"))
+  def testToCamel(self):
+    self.assertEquals("CamelCase", generator.ToCamel("camel_case"))
+    self.assertEquals("CAMELCASE", generator.ToCamel("CAMEL_CASE"))
+    self.assertEquals("camelCase", generator.ToCamel("camel_case",
+                                                     lower_initial=True))
+    self.assertEquals("CamelCase", generator.ToCamel("camel case",
+                                                     dilimiter=' '))
+    self.assertEquals("CaMelCaSe", generator.ToCamel("caMel_caSe"))
 
 if __name__ == "__main__":
   unittest.main()