Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Unified Diff: tools/json_schema_compiler/ppapi_generator.py

Issue 101483003: Add a Pepper IDL generator to the JSON schema compiler. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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)

Powered by Google App Engine
This is Rietveld 408576698