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

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

Powered by Google App Engine
This is Rietveld 408576698