h_generator.py revision 5f1c94371a64b3196d4be9466099bb892df9b88e
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import os
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from code import Code
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)from model import PropertyType
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import cpp_util
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import schema_util
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HGenerator(object):
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self, type_generator, cpp_namespace_pattern):
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._type_generator = type_generator
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._cpp_namespace_pattern = cpp_namespace_pattern
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def Generate(self, namespace):
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return _Generator(namespace,
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      self._type_generator,
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                      self._cpp_namespace_pattern).Generate()
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class _Generator(object):
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A .h generator for a namespace.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self, namespace, cpp_type_generator, cpp_namespace_pattern):
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._namespace = namespace
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._type_helper = cpp_type_generator
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._cpp_namespace_pattern = cpp_namespace_pattern
30c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch    self._generate_error_messages = namespace.compiler_options.get(
31c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch        'generate_error_messages', False)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Generate(self):
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates a Code object with the .h for a single namespace.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (c.Append(cpp_util.CHROMIUM_LICENSE)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append()
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append()
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Hack: for the purpose of gyp the header file will always be the source
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # file with its file extension replaced by '.h'. Assume so.
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    output_file = os.path.splitext(self._namespace.source_file)[0] + '.h'
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ifndef_name = cpp_util.GenerateIfndefName(output_file)
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (c.Append('#ifndef %s' % ifndef_name)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#define %s' % ifndef_name)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append()
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append('#include <map>')
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include <string>')
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include <vector>')
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append()
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include "base/basictypes.h"')
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include "base/logging.h"')
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include "base/memory/linked_ptr.h"')
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include "base/memory/scoped_ptr.h"')
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#include "base/values.h"')
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Cblock(self._type_helper.GenerateIncludes())
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append()
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(calamity): These forward declarations should be #includes to allow
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # $ref types from other files to be used as required params. This requires
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # some detangling of windows and tabs which will currently lead to circular
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # #includes.
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    c.Cblock(self._type_helper.GenerateForwardDeclarations(
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._cpp_namespace_pattern))
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cpp_namespace = cpp_util.GetCppNamespace(self._cpp_namespace_pattern,
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                             self._namespace.unix_name)
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    c.Concat(cpp_util.OpenNamespace(cpp_namespace))
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c.Append()
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._namespace.properties:
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Append('//')
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('// Properties')
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('//')
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append()
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for property in self._namespace.properties.values():
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_code = self._type_helper.GeneratePropertyValues(
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            property,
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            'extern const %(type)s %(name)s;')
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if property_code:
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          c.Cblock(property_code)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._namespace.types:
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Append('//')
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('// Types')
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('//')
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append()
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Cblock(self._GenerateTypes(self._FieldDependencyOrder(),
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    is_toplevel=True,
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    generate_typedefs=True))
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._namespace.functions:
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Append('//')
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('// Functions')
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('//')
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append()
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for function in self._namespace.functions.values():
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        c.Cblock(self._GenerateFunction(function))
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._namespace.events:
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Append('//')
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('// Events')
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append('//')
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Append()
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for event in self._namespace.events.values():
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        c.Cblock(self._GenerateEvent(event))
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    (c.Concat(cpp_util.CloseNamespace(cpp_namespace))
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append('#endif  // %s' % ifndef_name)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .Append()
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _FieldDependencyOrder(self):
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates the list of types in the current namespace in an order in which
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    depended-upon types appear before types which depend on them.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dependency_order = []
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def ExpandType(path, type_):
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if type_ in path:
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise ValueError("Illegal circular dependency via cycle " +
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         ", ".join(map(lambda x: x.name, path + [type_])))
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for prop in type_.properties.values():
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (prop.type_ == PropertyType.REF and
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            schema_util.GetNamespace(prop.ref_type) == self._namespace.name):
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ExpandType(path + [type_], self._namespace.types[prop.ref_type])
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not type_ in dependency_order:
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dependency_order.append(type_)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for type_ in self._namespace.types.values():
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExpandType([], type_)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return dependency_order
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateEnumDeclaration(self, enum_name, type_):
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    """Generate a code object with the  declaration of a C++ enum.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c.Sblock('enum %s {' % enum_name)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    c.Append(self._type_helper.GetEnumNoneValue(type_) + ',')
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for value in type_.enum_values:
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      current_enum_string = self._type_helper.GetEnumValue(type_, value)
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      c.Append(current_enum_string + ',')
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    c.Append('%s = %s,' % (
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        self._type_helper.GetEnumLastValue(type_), current_enum_string))
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    c.Eblock('};')
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return c
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFields(self, props):
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates the field declarations when declaring a type.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    needs_blank_line = False
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for prop in props:
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if needs_blank_line:
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        c.Append()
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      needs_blank_line = True
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if prop.description:
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        c.Comment(prop.description)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # ANY is a base::Value which is abstract and cannot be a direct member, so
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # we always need to wrap it in a scoped_ptr.
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Append('%s %s;' % (
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           self._type_helper.GetCppType(prop.type_, is_ptr=is_ptr),
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           prop.unix_name))
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False):
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Generates a struct for |type_|.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    |is_toplevel|       implies that the type was declared in the "types" field
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        of an API schema. This determines the correct function
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        modifier(s).
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    |generate_typedefs| controls whether primitive types should be generated as
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        a typedef. This may not always be desired. If false,
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        primitive types are ignored.
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if type_.functions:
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Wrap functions within types in the type's namespace.
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (c.Append('namespace %s {' % classname)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append()
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      )
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for function in type_.functions.values():
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        c.Cblock(self._GenerateFunction(function))
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      c.Append('}  // namespace %s' % classname)
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == PropertyType.ARRAY:
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if generate_typedefs and type_.description:
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        c.Comment(type_.description)
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      c.Cblock(self._GenerateType(type_.item_type))
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if generate_typedefs:
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (c.Append('typedef std::vector<%s > %s;' % (
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       self._type_helper.GetCppType(type_.item_type),
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       classname))
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        )
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == PropertyType.STRING:
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if generate_typedefs:
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if type_.description:
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          c.Comment(type_.description)
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        c.Append('typedef std::string %(classname)s;')
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == PropertyType.ENUM:
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if type_.description:
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        c.Comment(type_.description)
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      c.Cblock(self._GenerateEnumDeclaration(classname, type_));
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Top level enums are in a namespace scope so the methods shouldn't be
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # static. On the other hand, those declared inline (e.g. in an object) do.
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      maybe_static = '' if is_toplevel else 'static '
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      (c.Append()
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append('%sstd::string ToString(%s as_enum);' %
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                (maybe_static, classname))
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append('%s%s Parse%s(const std::string& as_string);' %
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                (maybe_static, classname, classname))
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
2217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    elif type_.property_type in (PropertyType.CHOICES,
2227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                 PropertyType.OBJECT):
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if type_.description:
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        c.Comment(type_.description)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Sblock('struct %(classname)s {')
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          .Append('%(classname)s();')
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          .Append('~%(classname)s();')
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if type_.origin.from_json:
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (c.Append()
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          .Comment('Populates a %s object from a base::Value. Returns'
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   ' whether |out| was successfully populated.' % classname)
233c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch          .Append('static bool Populate(%s);' % self._GenerateParams(
234c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch              ('const base::Value& value', '%s* out' % classname)))
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        )
236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if is_toplevel:
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          (c.Append()
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            .Comment('Creates a %s object from a base::Value, or NULL on '
239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                     'failure.' % classname)
240c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch            .Append('static scoped_ptr<%s> FromValue(%s);' % (
241c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch                classname, self._GenerateParams(('const base::Value& value',))))
242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          )
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if type_.origin.from_client:
2447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        value_type = ('base::Value'
2457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                      if type_.property_type is PropertyType.CHOICES else
2467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                      'base::DictionaryValue')
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (c.Append()
2487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          .Comment('Returns a new %s representing the serialized form of this '
2497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                   '%s object.' % (value_type, classname))
2507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          .Append('scoped_ptr<%s> ToValue() const;' % value_type)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        )
2527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if type_.property_type == PropertyType.CHOICES:
2537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        # Choices are modelled with optional fields for each choice. Exactly one
2547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        # field of the choice is guaranteed to be set by the compiler.
2557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        c.Cblock(self._GenerateTypes(type_.choices))
2567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        c.Append('// Choices:')
2577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        for choice_type in type_.choices:
2587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          c.Append('%s as_%s;' % (
2597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch              self._type_helper.GetCppType(choice_type, is_ptr=True),
2607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch              choice_type.unix_name))
2617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      else:
2627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        properties = type_.properties.values()
2637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        (c.Append()
2647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          .Cblock(self._GenerateTypes(p.type_ for p in properties))
2657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          .Cblock(self._GenerateFields(properties)))
2667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        if type_.additional_properties is not None:
2677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          # Most additionalProperties actually have type "any", which is better
2687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          # modelled as a DictionaryValue rather than a map of string -> Value.
2697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          if type_.additional_properties.property_type == PropertyType.ANY:
2707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            c.Append('base::DictionaryValue additional_properties;')
2717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          else:
2727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            (c.Cblock(self._GenerateType(type_.additional_properties))
2737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch              .Append('std::map<std::string, %s> additional_properties;' %
2747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                  cpp_util.PadForGenerics(
2757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                      self._type_helper.GetCppType(type_.additional_properties,
2767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                                   is_in_container=True)))
2777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            )
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (c.Eblock()
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append()
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Sblock(' private:')
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);')
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .Eblock('};')
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
2847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return c.Substitute({'classname': classname})
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateEvent(self, event):
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates the namespaces for an event.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # TODO(kalman): use event.unix_name not Classname.
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    event_namespace = cpp_util.Classname(event.name)
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (c.Append('namespace %s {' % event_namespace)
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append()
2942385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch      .Concat(self._GenerateEventNameConstant(event))
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Concat(self._GenerateCreateCallbackArguments(event))
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Eblock('}  // namespace %s' % event_namespace)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunction(self, function):
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates the namespaces and structs for a function.
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # TODO(kalman): Use function.unix_name not Classname here.
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    function_namespace = cpp_util.Classname(function.name)
30658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    # Windows has a #define for SendMessage, so to avoid any issues, we need
30758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    # to not use the name.
30890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if function_namespace == 'SendMessage':
30990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      function_namespace = 'PassMessage'
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (c.Append('namespace %s {' % function_namespace)
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append()
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Cblock(self._GenerateFunctionParams(function))
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if function.callback:
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      c.Cblock(self._GenerateFunctionResults(function.callback))
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    c.Append('}  // namespace %s' % function_namespace)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunctionParams(self, function):
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates the struct for passing parameters from JSON to a function.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not function.params:
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return Code()
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    c = Code()
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (c.Sblock('struct Params {')
327c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch      .Append('static scoped_ptr<Params> Create(%s);' % self._GenerateParams(
328c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch          ('const base::ListValue& args',)))
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append('~Params();')
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append()
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Cblock(self._GenerateTypes(p.type_ for p in function.params))
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Cblock(self._GenerateFields(function.params))
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Eblock()
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append()
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Sblock(' private:')
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append('Params();')
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append()
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        .Append('DISALLOW_COPY_AND_ASSIGN(Params);')
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Eblock('};')
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    )
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateTypes(self, types, is_toplevel=False, generate_typedefs=False):
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generate the structures required by a property such as OBJECT classes
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    and enums.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for type_ in types:
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      c.Cblock(self._GenerateType(type_,
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  is_toplevel=is_toplevel,
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  generate_typedefs=generate_typedefs))
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateCreateCallbackArguments(self, function):
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Generates functions for passing parameters to a callback.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params = function.params
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    c.Cblock(self._GenerateTypes((p.type_ for p in params), is_toplevel=True))
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    declaration_list = []
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for param in params:
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if param.description:
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        c.Comment(param.description)
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      declaration_list.append(cpp_util.GetParameterDeclaration(
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          param, self._type_helper.GetCppType(param.type_)))
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    c.Append('scoped_ptr<base::ListValue> Create(%s);' %
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             ', '.join(declaration_list))
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3712385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch  def _GenerateEventNameConstant(self, event):
3722385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    """Generates a constant string array for the event name.
3732385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    """
3742385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    c = Code()
3752385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    c.Append('extern const char kEventName[];  // "%s.%s"' % (
3762385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch                 self._namespace.name, event.name))
3772385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    c.Append()
3782385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    return c
3792385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunctionResults(self, callback):
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Generates namespace for passing a function's result back.
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    c = Code()
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (c.Append('namespace Results {')
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append()
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Concat(self._GenerateCreateCallbackArguments(callback))
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      .Append('}  // namespace Results')
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return c
390c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch
391c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch  def _GenerateParams(self, params):
392c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch    """Builds the parameter list for a function, given an array of parameters.
393c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch    """
394a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # |error| is populated with warnings and/or errors found during parsing.
395a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # |error| being set does not necessarily imply failure and may be
396a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # recoverable.
397a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # For example, optional properties may have failed to parse, but the
398a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # parser was able to continue.
399c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch    if self._generate_error_messages:
400010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      params += ('base::string16* error',)
401c2db58bd994c04d98e4ee2cd7565b71548655fe3Ben Murdoch    return ', '.join(str(p) for p in params)
402