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

Side by Side Diff: mojo/public/tools/bindings/generators/mojom_python_generator.py

Issue 814543006: Move //mojo/{public, edk} underneath //third_party (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 11 months 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 2014 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 """Generates Python source files from a mojom.Module."""
6
7 import re
8 from itertools import ifilter
9
10 import mojom.generate.generator as generator
11 import mojom.generate.data as data
12 import mojom.generate.module as mojom
13 from mojom.generate.template_expander import UseJinja
14
15 _kind_to_type = {
16 mojom.BOOL: '_descriptor.TYPE_BOOL',
17 mojom.INT8: '_descriptor.TYPE_INT8',
18 mojom.UINT8: '_descriptor.TYPE_UINT8',
19 mojom.INT16: '_descriptor.TYPE_INT16',
20 mojom.UINT16: '_descriptor.TYPE_UINT16',
21 mojom.INT32: '_descriptor.TYPE_INT32',
22 mojom.UINT32: '_descriptor.TYPE_UINT32',
23 mojom.INT64: '_descriptor.TYPE_INT64',
24 mojom.UINT64: '_descriptor.TYPE_UINT64',
25 mojom.FLOAT: '_descriptor.TYPE_FLOAT',
26 mojom.DOUBLE: '_descriptor.TYPE_DOUBLE',
27 mojom.STRING: '_descriptor.TYPE_STRING',
28 mojom.NULLABLE_STRING: '_descriptor.TYPE_NULLABLE_STRING',
29 mojom.HANDLE: '_descriptor.TYPE_HANDLE',
30 mojom.DCPIPE: '_descriptor.TYPE_HANDLE',
31 mojom.DPPIPE: '_descriptor.TYPE_HANDLE',
32 mojom.MSGPIPE: '_descriptor.TYPE_HANDLE',
33 mojom.SHAREDBUFFER: '_descriptor.TYPE_HANDLE',
34 mojom.NULLABLE_HANDLE: '_descriptor.TYPE_NULLABLE_HANDLE',
35 mojom.NULLABLE_DCPIPE: '_descriptor.TYPE_NULLABLE_HANDLE',
36 mojom.NULLABLE_DPPIPE: '_descriptor.TYPE_NULLABLE_HANDLE',
37 mojom.NULLABLE_MSGPIPE: '_descriptor.TYPE_NULLABLE_HANDLE',
38 mojom.NULLABLE_SHAREDBUFFER: '_descriptor.TYPE_NULLABLE_HANDLE',
39 }
40
41 # int64 integers are not handled by array.array. int64/uint64 array are
42 # supported but storage is not optimized (ie. they are plain python list, not
43 # array.array)
44 _kind_to_typecode_for_native_array = {
45 mojom.INT8: 'b',
46 mojom.UINT8: 'B',
47 mojom.INT16: 'h',
48 mojom.UINT16: 'H',
49 mojom.INT32: 'i',
50 mojom.UINT32: 'I',
51 mojom.FLOAT: 'f',
52 mojom.DOUBLE: 'd',
53 }
54
55
56 def NameToComponent(name):
57 # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
58 # HTTP_Entry2_FooBar)
59 name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
60 # insert '_' between non upper and start of upper blocks (e.g.,
61 # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
62 name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
63 return [x.lower() for x in name.split('_')]
64
65 def UpperCamelCase(name):
66 return ''.join([x.capitalize() for x in NameToComponent(name)])
67
68 def CamelCase(name):
69 uccc = UpperCamelCase(name)
70 return uccc[0].lower() + uccc[1:]
71
72 def ConstantStyle(name):
73 components = NameToComponent(name)
74 if components[0] == 'k':
75 components = components[1:]
76 return '_'.join([x.upper() for x in components])
77
78 def FieldStyle(name):
79 components = NameToComponent(name)
80 return '_'.join([x.lower() for x in components])
81
82 def GetNameForElement(element):
83 if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or
84 mojom.IsStructKind(element) or isinstance(element, mojom.Method)):
85 return UpperCamelCase(element.name)
86 if isinstance(element, mojom.EnumValue):
87 return (GetNameForElement(element.enum) + '.' +
88 ConstantStyle(element.name))
89 if isinstance(element, (mojom.NamedValue,
90 mojom.Constant)):
91 return ConstantStyle(element.name)
92 if isinstance(element, mojom.Field):
93 return FieldStyle(element.name)
94 raise Exception('Unexpected element: %s' % element)
95
96 def ExpressionToText(token):
97 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
98 return str(token.computed_value)
99
100 if isinstance(token, mojom.BuiltinValue):
101 if token.value == 'double.INFINITY' or token.value == 'float.INFINITY':
102 return 'float(\'inf\')';
103 if (token.value == 'double.NEGATIVE_INFINITY' or
104 token.value == 'float.NEGATIVE_INFINITY'):
105 return 'float(\'-inf\')'
106 if token.value == 'double.NAN' or token.value == 'float.NAN':
107 return 'float(\'nan\')';
108
109 if token in ['true', 'false']:
110 return str(token == 'true')
111
112 return token
113
114 def GetFullyQualifiedName(kind):
115 name = []
116 if kind.imported_from:
117 name.append(kind.imported_from['python_module'])
118 name.append(GetNameForElement(kind))
119 return '.'.join(name)
120
121 def GetFieldType(kind, field=None):
122 if mojom.IsArrayKind(kind):
123 arguments = []
124 if kind.kind in _kind_to_typecode_for_native_array:
125 arguments.append('%r' % _kind_to_typecode_for_native_array[kind.kind])
126 elif kind.kind != mojom.BOOL:
127 arguments.append(GetFieldType(kind.kind))
128 if mojom.IsNullableKind(kind):
129 arguments.append('nullable=True')
130 if kind.length is not None:
131 arguments.append('length=%d' % kind.length)
132 array_type = 'GenericArrayType'
133 if kind.kind == mojom.BOOL:
134 array_type = 'BooleanArrayType'
135 elif kind.kind in _kind_to_typecode_for_native_array:
136 array_type = 'NativeArrayType'
137 return '_descriptor.%s(%s)' % (array_type, ', '.join(arguments))
138
139 if mojom.IsMapKind(kind):
140 arguments = [
141 GetFieldType(kind.key_kind),
142 GetFieldType(kind.value_kind),
143 ]
144 if mojom.IsNullableKind(kind):
145 arguments.append('nullable=True')
146 return '_descriptor.MapType(%s)' % ', '.join(arguments)
147
148 if mojom.IsStructKind(kind):
149 arguments = [ 'lambda: %s' % GetFullyQualifiedName(kind) ]
150 if mojom.IsNullableKind(kind):
151 arguments.append('nullable=True')
152 return '_descriptor.StructType(%s)' % ', '.join(arguments)
153
154 if mojom.IsEnumKind(kind):
155 return GetFieldType(mojom.INT32)
156
157 if mojom.IsInterfaceKind(kind):
158 arguments = [ 'lambda: %s' % GetFullyQualifiedName(kind) ]
159 if mojom.IsNullableKind(kind):
160 arguments.append('nullable=True')
161 return '_descriptor.InterfaceType(%s)' % ', '.join(arguments)
162
163 if mojom.IsInterfaceRequestKind(kind):
164 arguments = []
165 if mojom.IsNullableKind(kind):
166 arguments.append('nullable=True')
167 return '_descriptor.InterfaceRequestType(%s)' % ', '.join(arguments)
168
169 return _kind_to_type[kind]
170
171 def GetFieldDescriptor(packed_field):
172 field = packed_field.field
173 class_name = 'SingleFieldGroup'
174 if field.kind == mojom.BOOL:
175 class_name = 'FieldDescriptor'
176 arguments = [ '%r' % GetNameForElement(field) ]
177 arguments.append(GetFieldType(field.kind, field))
178 arguments.append(str(packed_field.index))
179 arguments.append(str(packed_field.ordinal))
180 if field.default:
181 if mojom.IsStructKind(field.kind):
182 arguments.append('default_value=True')
183 else:
184 arguments.append('default_value=%s' % ExpressionToText(field.default))
185 return '_descriptor.%s(%s)' % (class_name, ', '.join(arguments))
186
187 def GetFieldGroup(byte):
188 if byte.packed_fields[0].field.kind == mojom.BOOL:
189 descriptors = map(GetFieldDescriptor, byte.packed_fields)
190 return '_descriptor.BooleanGroup([%s])' % ', '.join(descriptors)
191 assert len(byte.packed_fields) == 1
192 return GetFieldDescriptor(byte.packed_fields[0])
193
194 def GetResponseStructFromMethod(method):
195 return generator.GetDataHeader(
196 False, generator.GetResponseStructFromMethod(method))
197
198 def GetStructFromMethod(method):
199 return generator.GetDataHeader(
200 False, generator.GetStructFromMethod(method))
201
202 def ComputeStaticValues(module):
203 in_progress = set()
204 computed = set()
205
206 def GetComputedValue(named_value):
207 if isinstance(named_value, mojom.EnumValue):
208 field = next(ifilter(lambda field: field.name == named_value.name,
209 named_value.enum.fields), None)
210 if not field:
211 raise RuntimeError(
212 'Unable to get computed value for field %s of enum %s' %
213 (named_value.name, named_value.enum.name))
214 if field not in computed:
215 ResolveEnum(named_value.enum)
216 return field.computed_value
217 elif isinstance(named_value, mojom.ConstantValue):
218 ResolveConstant(named_value.constant)
219 named_value.computed_value = named_value.constant.computed_value
220 return named_value.computed_value
221 else:
222 print named_value
223
224 def ResolveConstant(constant):
225 if constant in computed:
226 return
227 if constant in in_progress:
228 raise RuntimeError('Circular dependency for constant: %s' % constant.name)
229 in_progress.add(constant)
230 if isinstance(constant.value, (mojom.EnumValue, mojom.ConstantValue)):
231 computed_value = GetComputedValue(constant.value)
232 else:
233 computed_value = ExpressionToText(constant.value)
234 constant.computed_value = computed_value
235 in_progress.remove(constant)
236 computed.add(constant)
237
238 def ResolveEnum(enum):
239 def ResolveEnumField(enum, field, default_value):
240 if field in computed:
241 return
242 if field in in_progress:
243 raise RuntimeError('Circular dependency for enum: %s' % enum.name)
244 in_progress.add(field)
245 if field.value:
246 if isinstance(field.value, mojom.EnumValue):
247 computed_value = GetComputedValue(field.value)
248 elif isinstance(field.value, str):
249 computed_value = int(field.value, 0)
250 else:
251 raise RuntimeError('Unexpected value: %s' % field.value)
252 else:
253 computed_value = default_value
254 field.computed_value = computed_value
255 in_progress.remove(field)
256 computed.add(field)
257
258 current_value = 0
259 for field in enum.fields:
260 ResolveEnumField(enum, field, current_value)
261 current_value = field.computed_value + 1
262
263 for constant in module.constants:
264 ResolveConstant(constant)
265
266 for enum in module.enums:
267 ResolveEnum(enum)
268
269 for struct in module.structs:
270 for constant in struct.constants:
271 ResolveConstant(constant)
272 for enum in struct.enums:
273 ResolveEnum(enum)
274 for field in struct.fields:
275 if isinstance(field.default, (mojom.ConstantValue, mojom.EnumValue)):
276 field.default.computed_value = GetComputedValue(field.default)
277
278 return module
279
280 def MojomToPythonImport(mojom):
281 return mojom.replace('.mojom', '_mojom')
282
283 class Generator(generator.Generator):
284
285 python_filters = {
286 'expression_to_text': ExpressionToText,
287 'field_group': GetFieldGroup,
288 'fully_qualified_name': GetFullyQualifiedName,
289 'name': GetNameForElement,
290 'response_struct_from_method': GetResponseStructFromMethod,
291 'struct_from_method': GetStructFromMethod,
292 }
293
294 @UseJinja('python_templates/module.py.tmpl', filters=python_filters)
295 def GeneratePythonModule(self):
296 return {
297 'enums': self.module.enums,
298 'imports': self.GetImports(),
299 'interfaces': self.GetQualifiedInterfaces(),
300 'module': ComputeStaticValues(self.module),
301 'structs': self.GetStructs(),
302 }
303
304 def GenerateFiles(self, args):
305 import_path = MojomToPythonImport(self.module.name)
306 self.Write(self.GeneratePythonModule(),
307 self.MatchMojomFilePath('%s.py' % import_path))
308
309 def GetImports(self):
310 for each in self.module.imports:
311 each['python_module'] = MojomToPythonImport(each['module_name'])
312 return self.module.imports
313
314 def GetQualifiedInterfaces(self):
315 """
316 Returns the list of interfaces of the module. Each interface that has a
317 client will have a reference to the representation of the client interface
318 in the 'qualified_client' field.
319 """
320 interfaces = self.module.interfaces
321 all_interfaces = [] + interfaces
322 for each in self.GetImports():
323 all_interfaces += [data.KindFromImport(x, each) for x in
324 each['module'].interfaces];
325 interfaces_by_name = dict((x.name, x) for x in all_interfaces)
326 for interface in interfaces:
327 if interface.client:
328 assert interface.client in interfaces_by_name, (
329 'Unable to find interface %s declared as client of %s.' %
330 (interface.client, interface.name))
331 interface.qualified_client = interfaces_by_name[interface.client]
332 return sorted(interfaces, key=lambda i: (bool(i.client), i.name))
333
334 def GetJinjaParameters(self):
335 return {
336 'lstrip_blocks': True,
337 'trim_blocks': True,
338 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698