Chromium Code Reviews| Index: tools/json_schema_compiler/ppapi_generator.py |
| diff --git a/tools/json_schema_compiler/ppapi_generator.py b/tools/json_schema_compiler/ppapi_generator.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8faee873666c85b949965674741d8cdb54414470 |
| --- /dev/null |
| +++ b/tools/json_schema_compiler/ppapi_generator.py |
| @@ -0,0 +1,256 @@ |
| +# 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 collections |
| +import datetime |
| +import os.path |
| + |
| +import code |
| +import cpp_util |
| +import model |
| + |
| +try: |
| + import jinja2 |
| +except ImportError as e: |
| + jinja2 = None |
| + jinja2_error = e |
| + |
| + |
| +class _PpapiGeneratorBase(object): |
|
not at google - send to devlin
2013/12/09 23:30:58
docstring
Sam McNally
2013/12/10 09:41:15
Done.
|
| + def __init__(self, namespace, is_dev): |
| + self.is_dev = is_dev |
| + self._namespace = namespace |
| + self._required_types = {} |
| + self._array_types = set() |
| + self._optional_types = set() |
| + self._optional_array_types = set() |
| + self._dependencies = collections.OrderedDict() |
| + self._types = [] |
| + self._enums = [] |
| + |
| + if not jinja2: |
| + raise jinja2_error |
|
not at google - send to devlin
2013/12/09 23:30:58
this will reset the stack trace. if you want the o
Sam McNally
2013/12/10 09:41:15
Done.
|
| + self.jinja_environment = jinja2.Environment( |
| + loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__), |
| + 'templates', 'ppapi'))) |
| + self._SetupFilters() |
| + self._ResolveTypeDependencies() |
| + |
| + def _SetupFilters(self): |
| + self.jinja_environment.filters['ppapi_type'] = self.ToPpapiType |
| + self.jinja_environment.filters['classname'] = cpp_util.Classname |
| + self.jinja_environment.filters['dev_suffix'] = self.AddDevSuffix |
| + self.jinja_environment.filters['enum_value'] = self.EnumValueName |
| + self.jinja_environment.filters['return_type'] = self.GetFunctionReturnType |
| + self.jinja_environment.filters['format_param_type'] = self.FormatParamType |
| + self.jinja_environment.filters['needs_optional'] = self.NeedsOptional |
| + self.jinja_environment.filters['needs_array'] = self.NeedsArray |
| + self.jinja_environment.filters['needs_optional_array'] = ( |
|
not at google - send to devlin
2013/12/09 23:30:58
nit: self.jinja_environment.filters.update({
'pp
Sam McNally
2013/12/10 09:41:15
Done.
|
| + self.NeedsOptionalArray) |
| + |
| + def Render(self, template_name, values): |
| + c = code.Code() |
| + template = self.jinja_environment.get_template( |
| + '%s.template' % template_name) |
| + c.Append(template.render(values)) |
| + return c |
| + |
| + def Generate(self): |
| + """Generates a Code object for a single namespace.""" |
| + return self.Render(self.TEMPLATE_NAME, { |
| + 'name': self._namespace.name, |
|
not at google - send to devlin
2013/12/09 23:30:58
nit: only 2 indent for object declarations not 4.
Sam McNally
2013/12/10 09:41:15
Done.
|
| + 'enums': self._enums, |
| + 'types': self._types, |
| + 'events': self._namespace.events, |
| + 'functions': self._namespace.functions, |
| + 'year': datetime.date.today().year, |
| + 'source_file': self._namespace.source_file, |
| + 'dev': '_dev' if self.is_dev else '', |
| + 'dev_path': '/dev' if self.is_dev else '', |
| + }) |
| + |
| + def _ResolveTypeDependencies(self): |
| + """Calculates the transitive closure of the types in _required_types. |
| + |
| + Returns a tuple containing the list of struct types and the list of enum |
| + types. The list of struct types is ordered such that no type depends on a |
| + type later in the list. |
| + |
| + """ |
| + if self._namespace.functions: |
| + for function in self._namespace.functions.values(): |
|
not at google - send to devlin
2013/12/09 23:30:58
itervalue)
and iterfoo() for everywhere else it m
Sam McNally
2013/12/10 09:41:15
Done.
|
| + self._FindFunctionDependencies(function) |
| + |
| + if self._namespace.events: |
| + for event in self._namespace.events.values(): |
| + self._FindFunctionDependencies(event) |
| + resolved_types = set() |
| + while resolved_types < set(self._required_types): |
| + for typename in sorted(set(self._required_types) - resolved_types): |
| + type_ = self._required_types[typename] |
| + self._dependencies.setdefault(typename, set()) |
| + for member in type_.properties.values(): |
| + self._RegisterDependency(member, self._NameComponents(type_)) |
| + resolved_types.add(typename) |
| + while self._dependencies: |
| + for name, deps in self._dependencies.items(): |
| + if not deps: |
| + if (self._required_types[name].property_type == |
| + model.PropertyType.ENUM): |
| + self._enums.append(self._required_types[name]) |
| + else: |
| + self._types.append(self._required_types[name]) |
| + for deps in self._dependencies.values(): |
| + deps.discard(name) |
| + del self._dependencies[name] |
| + break |
| + else: |
| + raise ValueError('Circular dependency %s' % self._dependencies) |
| + |
| + def _FindFunctionDependencies(self, function): |
| + for param in function.params: |
| + self._RegisterDependency(param, None) |
| + if function.callback: |
| + for param in function.callback.params: |
| + self._RegisterDependency(param, None) |
| + if function.returns: |
| + self._RegisterTypeDependency(function.returns, None, False, False) |
| + |
| + def _RegisterDependency(self, member, depender): |
| + self._RegisterTypeDependency(member.type_, depender, member.optional, False) |
| + |
| + def _RegisterTypeDependency(self, type_, depender, optional, array): |
| + if type_.property_type == model.PropertyType.ARRAY: |
| + self._RegisterTypeDependency(type_.item_type, depender, optional, True) |
| + elif type_.property_type == model.PropertyType.REF: |
| + self._RegisterTypeDependency(self._namespace.types[type_.ref_type], |
| + depender, optional, array) |
| + elif type_.property_type in (model.PropertyType.OBJECT, |
| + model.PropertyType.ENUM): |
| + name_components = self._NameComponents(type_) |
| + self._required_types[name_components] = type_ |
| + if depender: |
| + self._dependencies.setdefault(depender, set()).add( |
| + name_components) |
| + if array: |
| + self._array_types.add(name_components) |
| + if optional: |
| + self._optional_array_types.add(name_components) |
| + elif optional: |
| + self._optional_types.add(name_components) |
| + |
| + @staticmethod |
| + def _NameComponents(entity, include_namespace=False): |
| + """Returns a tuple of the fully-qualified name of an entity.""" |
| + names = [] |
| + while entity: |
| + if (not isinstance(entity, model.Type) or |
| + entity.property_type != model.PropertyType.ARRAY): |
| + names.append(entity.name) |
| + entity = entity.parent |
| + if include_namespace: |
| + return tuple(reversed(names)) |
| + return tuple(reversed(names[:-1])) |
| + |
| + def ToPpapiType(self, type_, array=False, optional=False): |
| + """Returns a string containing the name of the Pepper C type for |type_|. |
| + |
| + If array is True, returns the name of an array of |type_|. If optional is |
| + True, returns the name of an optional |type_|. If both array and optional |
| + are True, returns the name of an optional array of |type_|. |
| + """ |
| + if isinstance(type_, model.Function) or type_.property_type in ( |
| + model.PropertyType.OBJECT, model.PropertyType.ENUM): |
| + return self._FormatPpapiTypeName( |
| + array, optional, '_'.join( |
| + cpp_util.Classname(s) for s in self._NameComponents( |
| + type_, include_namespace=True)), |
| + '_Dev' if self.is_dev else '') |
| + elif type_.property_type == model.PropertyType.REF: |
| + return self.ToPpapiType(self._namespace.types[type_.ref_type], |
| + optional=optional, array=array) |
| + elif type_.property_type == model.PropertyType.ARRAY: |
| + return self.ToPpapiType(type_.item_type, array=True, |
| + optional=optional) |
| + elif type_.property_type == model.PropertyType.STRING and not array: |
| + return 'PP_Var' |
| + elif array or optional: |
| + if type_.property_type in self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP: |
| + return self._FormatPpapiTypeName( |
| + array, optional, |
| + self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP[type_.property_type], '') |
| + return self._PPAPI_PRIMITIVE_TYPE_MAP.get(type_.property_type, 'PP_Var') |
| + |
| + _PPAPI_PRIMITIVE_TYPE_MAP = { |
| + model.PropertyType.BOOLEAN: 'PP_Bool', |
| + model.PropertyType.DOUBLE: 'double_t', |
| + model.PropertyType.INT64: 'int64_t', |
| + model.PropertyType.INTEGER: 'int32_t', |
| + } |
| + _PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP = { |
| + model.PropertyType.BOOLEAN: 'Bool', |
| + model.PropertyType.DOUBLE: 'Double', |
| + model.PropertyType.INT64: 'Int64', |
| + model.PropertyType.INTEGER: 'Int32', |
| + model.PropertyType.STRING: 'String', |
| + } |
| + |
| + @staticmethod |
| + def _FormatPpapiTypeName(array, optional, *args): |
| + if array: |
| + if optional: |
| + return 'PP_Optional_%s_Array%s' % args |
| + return 'PP_%s_Array%s' % args |
| + if optional: |
| + return 'PP_Optional_%s%s' % args |
| + return 'PP_%s%s' % args |
| + |
| + def NeedsOptional(self, type_): |
| + """Returns True if an optional |type_| is required.""" |
| + return self._NameComponents(type_) in self._optional_types |
| + |
| + def NeedsArray(self, type_): |
| + """Returns True if an array of |type_| is required.""" |
| + return self._NameComponents(type_) in self._array_types |
| + |
| + def NeedsOptionalArray(self, type_): |
| + """Returns True if an optional array of |type_| is required.""" |
| + return self._NameComponents(type_) in self._optional_array_types |
| + |
| + def FormatParamType(self, param): |
| + """Formats the type of a parameter or property.""" |
| + return self.ToPpapiType(param.type_, optional=param.optional) |
| + |
| + def AddDevSuffix(self, name): |
| + """Returns |name| with a _Dev suffix if a dev API is being generated.""" |
| + if self.is_dev: |
| + name = '%s_Dev' % name |
| + return name |
| + |
| + @staticmethod |
| + def GetFunctionReturnType(function): |
| + return 'int32_t' if function.callback or function.returns else 'void' |
| + |
| + def EnumValueName(self, enum_value, enum_type): |
| + """Returns a string containing the name for an enum value.""" |
| + return '%s_%s' % (self.ToPpapiType(enum_type).upper(), |
| + enum_value.name.upper()) |
| + |
| + |
| +class _IdlGenerator(_PpapiGeneratorBase): |
| + TEMPLATE_NAME = 'idl' |
| + |
| + |
| +class _GeneratorWrapper(object): |
| + def __init__(self, generator_factory, is_dev): |
| + self._generator_factory = generator_factory |
| + self._is_dev = is_dev |
| + |
| + def Generate(self, namespace): |
| + return self._generator_factory(namespace, self._is_dev).Generate() |
| + |
| + |
| +class PpapiGenerator(object): |
| + def __init__(self, is_dev): |
| + self.idl_generator = _GeneratorWrapper(_IdlGenerator, is_dev) |