• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import collections
6import datetime
7import os.path
8import sys
9
10import code
11import cpp_util
12import model
13
14try:
15  import jinja2
16except ImportError:
17  sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..',
18                               'third_party'))
19  import jinja2
20
21
22class _PpapiGeneratorBase(object):
23  """A base class for ppapi generators.
24
25  Implementations should set TEMPLATE_NAME to a string containing the name of
26  the template file without its extension. The template will be rendered with
27  the following symbols available:
28    name: A string containing the name of the namespace.
29    enums: A list of enums within the namespace.
30    types: A list of types within the namespace, sorted such that no element
31        depends on an earlier element.
32    events: A dict of events within the namespace.
33    functions: A dict of functions within the namespace.
34    year: An int containing the current year.
35    source_file: The name of the input file.
36  """
37
38  def __init__(self, namespace):
39    self._namespace = namespace
40    self._required_types = {}
41    self._array_types = set()
42    self._optional_types = set()
43    self._optional_array_types = set()
44    self._dependencies = collections.OrderedDict()
45    self._types = []
46    self._enums = []
47
48    self.jinja_environment = jinja2.Environment(
49        loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__),
50                                                    'templates', 'ppapi')))
51    self._SetupFilters()
52    self._ResolveTypeDependencies()
53
54  def _SetupFilters(self):
55    self.jinja_environment.filters.update({
56      'ppapi_type': self.ToPpapiType,
57      'classname': cpp_util.Classname,
58      'enum_value': self.EnumValueName,
59      'return_type': self.GetFunctionReturnType,
60      'format_param_type': self.FormatParamType,
61      'needs_optional': self.NeedsOptional,
62      'needs_array': self.NeedsArray,
63      'needs_optional_array': self.NeedsOptionalArray,
64      'has_array_outs': self.HasArrayOuts,
65    })
66
67  def Render(self, template_name, values):
68    generated_code = code.Code()
69    template = self.jinja_environment.get_template(
70        '%s.template' % template_name)
71    generated_code.Append(template.render(values))
72    return generated_code
73
74  def Generate(self):
75    """Generates a Code object for a single namespace."""
76    return self.Render(self.TEMPLATE_NAME, {
77      'name': self._namespace.name,
78      'enums': self._enums,
79      'types': self._types,
80      'events': self._namespace.events,
81      'functions': self._namespace.functions,
82      # TODO(sammc): Don't change years when regenerating existing output files.
83      'year': datetime.date.today().year,
84      'source_file': self._namespace.source_file,
85    })
86
87  def _ResolveTypeDependencies(self):
88    """Calculates the transitive closure of the types in _required_types.
89
90    Returns a tuple containing the list of struct types and the list of enum
91    types. The list of struct types is ordered such that no type depends on a
92    type later in the list.
93
94    """
95    if self._namespace.functions:
96      for function in self._namespace.functions.itervalues():
97        self._FindFunctionDependencies(function)
98
99    if self._namespace.events:
100      for event in self._namespace.events.itervalues():
101        self._FindFunctionDependencies(event)
102    resolved_types = set()
103    while resolved_types < set(self._required_types):
104      for typename in sorted(set(self._required_types) - resolved_types):
105        type_ = self._required_types[typename]
106        self._dependencies.setdefault(typename, set())
107        for member in type_.properties.itervalues():
108          self._RegisterDependency(member, self._NameComponents(type_))
109        resolved_types.add(typename)
110    while self._dependencies:
111      for name, deps in self._dependencies.items():
112        if not deps:
113          if (self._required_types[name].property_type ==
114              model.PropertyType.ENUM):
115            self._enums.append(self._required_types[name])
116          else:
117            self._types.append(self._required_types[name])
118          for deps in self._dependencies.itervalues():
119            deps.discard(name)
120          del self._dependencies[name]
121          break
122      else:
123        raise ValueError('Circular dependency %s' % self._dependencies)
124
125  def _FindFunctionDependencies(self, function):
126    for param in function.params:
127      self._RegisterDependency(param, None)
128    if function.callback:
129      for param in function.callback.params:
130        self._RegisterDependency(param, None)
131    if function.returns:
132      self._RegisterTypeDependency(function.returns, None, False, False)
133
134  def _RegisterDependency(self, member, depender):
135    self._RegisterTypeDependency(member.type_, depender, member.optional, False)
136
137  def _RegisterTypeDependency(self, type_, depender, optional, array):
138    if type_.property_type == model.PropertyType.ARRAY:
139      self._RegisterTypeDependency(type_.item_type, depender, optional, True)
140    elif type_.property_type == model.PropertyType.REF:
141      self._RegisterTypeDependency(self._namespace.types[type_.ref_type],
142                                   depender, optional, array)
143    elif type_.property_type in (model.PropertyType.OBJECT,
144                                 model.PropertyType.ENUM):
145      name_components = self._NameComponents(type_)
146      self._required_types[name_components] = type_
147      if depender:
148        self._dependencies.setdefault(depender, set()).add(
149            name_components)
150      if array:
151        self._array_types.add(name_components)
152        if optional:
153          self._optional_array_types.add(name_components)
154      elif optional:
155        self._optional_types.add(name_components)
156
157  @staticmethod
158  def _NameComponents(entity):
159    """Returns a tuple of the fully-qualified name of an entity."""
160    names = []
161    while entity:
162      if (not isinstance(entity, model.Type) or
163          entity.property_type != model.PropertyType.ARRAY):
164        names.append(entity.name)
165      entity = entity.parent
166    return tuple(reversed(names[:-1]))
167
168  def ToPpapiType(self, type_, array=False, optional=False):
169    """Returns a string containing the name of the Pepper C type for |type_|.
170
171    If array is True, returns the name of an array of |type_|. If optional is
172    True, returns the name of an optional |type_|. If both array and optional
173    are True, returns the name of an optional array of |type_|.
174    """
175    if isinstance(type_, model.Function) or type_.property_type in (
176        model.PropertyType.OBJECT, model.PropertyType.ENUM):
177      return self._FormatPpapiTypeName(
178          array, optional, '_'.join(
179              cpp_util.Classname(s) for s in self._NameComponents(type_)),
180              namespace=cpp_util.Classname(self._namespace.name))
181    elif type_.property_type == model.PropertyType.REF:
182      return self.ToPpapiType(self._namespace.types[type_.ref_type],
183                              optional=optional, array=array)
184    elif type_.property_type == model.PropertyType.ARRAY:
185      return self.ToPpapiType(type_.item_type, array=True,
186                              optional=optional)
187    elif type_.property_type == model.PropertyType.STRING and not array:
188      return 'PP_Var'
189    elif array or optional:
190      if type_.property_type in self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP:
191        return self._FormatPpapiTypeName(
192            array, optional,
193            self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP[type_.property_type], '')
194    return self._PPAPI_PRIMITIVE_TYPE_MAP.get(type_.property_type, 'PP_Var')
195
196  _PPAPI_PRIMITIVE_TYPE_MAP = {
197    model.PropertyType.BOOLEAN: 'PP_Bool',
198    model.PropertyType.DOUBLE: 'double_t',
199    model.PropertyType.INT64: 'int64_t',
200    model.PropertyType.INTEGER: 'int32_t',
201  }
202  _PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP = {
203    model.PropertyType.BOOLEAN: 'Bool',
204    model.PropertyType.DOUBLE: 'Double',
205    model.PropertyType.INT64: 'Int64',
206    model.PropertyType.INTEGER: 'Int32',
207    model.PropertyType.STRING: 'String',
208  }
209
210  @staticmethod
211  def _FormatPpapiTypeName(array, optional, name, namespace=''):
212    if namespace:
213      namespace = '%s_' % namespace
214    if array:
215      if optional:
216        return 'PP_%sOptional_%s_Array' % (namespace, name)
217      return 'PP_%s%s_Array' % (namespace, name)
218    if optional:
219      return 'PP_%sOptional_%s' % (namespace, name)
220    return 'PP_%s%s' % (namespace, name)
221
222  def NeedsOptional(self, type_):
223    """Returns True if an optional |type_| is required."""
224    return self._NameComponents(type_) in self._optional_types
225
226  def NeedsArray(self, type_):
227    """Returns True if an array of |type_| is required."""
228    return self._NameComponents(type_) in self._array_types
229
230  def NeedsOptionalArray(self, type_):
231    """Returns True if an optional array of |type_| is required."""
232    return self._NameComponents(type_) in self._optional_array_types
233
234  def FormatParamType(self, param):
235    """Formats the type of a parameter or property."""
236    return self.ToPpapiType(param.type_, optional=param.optional)
237
238  @staticmethod
239  def GetFunctionReturnType(function):
240    return 'int32_t' if function.callback or function.returns else 'void'
241
242  def EnumValueName(self, enum_value, enum_type):
243    """Returns a string containing the name for an enum value."""
244    return '%s_%s' % (self.ToPpapiType(enum_type).upper(),
245                      enum_value.name.upper())
246
247  def _ResolveType(self, type_):
248    if type_.property_type == model.PropertyType.REF:
249      return self._ResolveType(self._namespace.types[type_.ref_type])
250    if type_.property_type == model.PropertyType.ARRAY:
251      return self._ResolveType(type_.item_type)
252    return type_
253
254  def _IsOrContainsArray(self, type_):
255    if type_.property_type == model.PropertyType.ARRAY:
256      return True
257    type_ = self._ResolveType(type_)
258    if type_.property_type == model.PropertyType.OBJECT:
259      return any(self._IsOrContainsArray(param.type_)
260                 for param in type_.properties.itervalues())
261    return False
262
263  def HasArrayOuts(self, function):
264    """Returns True if the function produces any arrays as outputs.
265
266    This includes arrays that are properties of other objects.
267    """
268    if function.callback:
269      for param in function.callback.params:
270        if self._IsOrContainsArray(param.type_):
271          return True
272    return function.returns and self._IsOrContainsArray(function.returns)
273
274
275class _IdlGenerator(_PpapiGeneratorBase):
276  TEMPLATE_NAME = 'idl'
277
278
279class _GeneratorWrapper(object):
280  def __init__(self, generator_factory):
281    self._generator_factory = generator_factory
282
283  def Generate(self, namespace):
284    return self._generator_factory(namespace).Generate()
285
286
287class PpapiGenerator(object):
288  def __init__(self):
289    self.idl_generator = _GeneratorWrapper(_IdlGenerator)
290