OLD | NEW |
---|---|
(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) | |
OLD | NEW |