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