OLD | NEW |
---|---|
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 '''Generates Go source files from a mojom.Module.''' | 5 '''Generates Go source files from a mojom.Module.''' |
6 | 6 |
7 from itertools import chain | 7 from itertools import chain |
8 import os | 8 import os |
9 import re | 9 import re |
10 | 10 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
50 'system.MessagePipeHandle', 'Handle', 'MessagePipeHandle', 32), | 50 'system.MessagePipeHandle', 'Handle', 'MessagePipeHandle', 32), |
51 mojom.NULLABLE_SHAREDBUFFER: KindInfo( | 51 mojom.NULLABLE_SHAREDBUFFER: KindInfo( |
52 'system.SharedBufferHandle', 'Handle', 'SharedBufferHandle', 32), | 52 'system.SharedBufferHandle', 'Handle', 'SharedBufferHandle', 32), |
53 mojom.INT64: KindInfo('int64', 'Int64', 'Int64', 64), | 53 mojom.INT64: KindInfo('int64', 'Int64', 'Int64', 64), |
54 mojom.UINT64: KindInfo('uint64', 'Uint64', 'Uint64', 64), | 54 mojom.UINT64: KindInfo('uint64', 'Uint64', 'Uint64', 64), |
55 mojom.DOUBLE: KindInfo('float64', 'Float64', 'Float64', 64), | 55 mojom.DOUBLE: KindInfo('float64', 'Float64', 'Float64', 64), |
56 mojom.STRING: KindInfo('string', 'String', 'String', 64), | 56 mojom.STRING: KindInfo('string', 'String', 'String', 64), |
57 mojom.NULLABLE_STRING: KindInfo('string', 'String', 'String', 64), | 57 mojom.NULLABLE_STRING: KindInfo('string', 'String', 'String', 64), |
58 } | 58 } |
59 | 59 |
60 # _imports keeps track of the imports that the .go.mojom file needs to import. | |
60 _imports = {} | 61 _imports = {} |
61 | 62 |
63 # _mojom_imports keeps a list of the other .mojom files imported by this one. | |
64 _mojom_imports = {} | |
65 | |
66 # The mojom_types.mojom and service_describer.mojom files are special because | |
67 # they are used to generate mojom Type's and ServiceDescription implementations. | |
68 _service_describer_pkg_short = "service_describer" | |
69 _service_describer_pkg = "mojo/public/interfaces/bindings/%s" % \ | |
70 _service_describer_pkg_short | |
71 _mojom_types_pkg_short = "mojom_types" | |
72 _mojom_types_pkg = "mojo/public/interfaces/bindings/%s" % _mojom_types_pkg_short | |
73 | |
62 def GetBitSize(kind): | 74 def GetBitSize(kind): |
63 if isinstance(kind, (mojom.Union)): | 75 if isinstance(kind, (mojom.Union)): |
64 return 128 | 76 return 128 |
65 if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct, mojom.Interface)): | 77 if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct, mojom.Interface)): |
66 return 64 | 78 return 64 |
67 if mojom.IsUnionKind(kind): | 79 if mojom.IsUnionKind(kind): |
68 return 2*64 | 80 return 2*64 |
69 if isinstance(kind, (mojom.InterfaceRequest)): | 81 if isinstance(kind, (mojom.InterfaceRequest)): |
70 kind = mojom.MSGPIPE | 82 kind = mojom.MSGPIPE |
71 if isinstance(kind, mojom.Enum): | 83 if isinstance(kind, mojom.Enum): |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
179 | 191 |
180 def EncodeSuffix(kind): | 192 def EncodeSuffix(kind): |
181 if mojom.IsEnumKind(kind): | 193 if mojom.IsEnumKind(kind): |
182 return EncodeSuffix(mojom.INT32) | 194 return EncodeSuffix(mojom.INT32) |
183 if mojom.IsInterfaceKind(kind): | 195 if mojom.IsInterfaceKind(kind): |
184 return 'Interface' | 196 return 'Interface' |
185 if mojom.IsInterfaceRequestKind(kind): | 197 if mojom.IsInterfaceRequestKind(kind): |
186 return EncodeSuffix(mojom.MSGPIPE) | 198 return EncodeSuffix(mojom.MSGPIPE) |
187 return _kind_infos[kind].encode_suffix | 199 return _kind_infos[kind].encode_suffix |
188 | 200 |
201 # This helper assists in the production of mojom_types.Type for simple kinds. | |
202 # See _kind_infos above. | |
203 def GetMojomTypeValue(kind, typepkg=''): | |
204 if not kind in _kind_infos: | |
205 return '' | |
206 | |
207 nullable = 'true' if mojom.IsNullableKind(kind) else 'false' | |
208 if kind == mojom.BOOL or kind == mojom.FLOAT or kind == mojom.DOUBLE or mojom. IsIntegralKind(kind): | |
209 kind_name = UpperCamelCase(_kind_infos[kind].decode_suffix.upper()) | |
210 if kind == mojom.FLOAT: | |
211 kind_name = "Float" | |
212 elif kind == mojom.DOUBLE: | |
213 kind_name = "Double" | |
214 return '%sTypeSimpleType{%sSimpleType_%s}' % (typepkg, typepkg, kind_name) | |
215 elif mojom.IsAnyHandleKind(kind): | |
216 kind_name = 'Unspecified' | |
217 if kind == mojom.DCPIPE: | |
218 kind_name = 'DataPipeConsumer' | |
219 elif kind == mojom.DPPIPE: | |
220 kind_name = 'DataPipeProducer' | |
221 elif kind == mojom.MSGPIPE: | |
222 kind_name = 'MessagePipe' | |
223 elif kind == mojom.SHAREDBUFFER: | |
224 kind_name = 'SharedBuffer' | |
225 return '%sTypeHandleType{%sHandleType{Nullable: %s, Kind: %sHandleType_Kind_ %s}}' % (typepkg, typepkg, nullable, typepkg, kind_name) | |
azani
2015/10/20 23:35:46
Line should be less than 80 chars long. (this appl
alexfandrianto
2015/10/21 00:12:46
I've wrapped those lines.
| |
226 elif mojom.IsStringKind(kind): | |
227 return '%sTypeStringType{%sStringType{%s}}' % (typepkg, typepkg, nullable) | |
228 else: | |
229 raise Exception('Missing case for kind: %s' % kind) | |
230 | |
189 def GetPackageName(module): | 231 def GetPackageName(module): |
190 return module.name.split('.')[0] | 232 return module.name.split('.')[0] |
191 | 233 |
192 def GetPackageNameForElement(element): | 234 def GetPackageNameForElement(element): |
193 if not hasattr(element, 'imported_from') or not element.imported_from: | 235 if not hasattr(element, 'imported_from') or not element.imported_from: |
194 return '' | 236 return '' |
195 path = '' | 237 path = '' |
196 if element.imported_from['module'].path: | 238 if element.imported_from['module'].path: |
197 path += GetPackagePath(element.imported_from['module']) | 239 path += GetPackagePath(element.imported_from['module']) |
198 if path in _imports: | 240 if path in _imports: |
(...skipping 14 matching lines...) Expand all Loading... | |
213 constants = [x.constants for x in data] | 255 constants = [x.constants for x in data] |
214 return [i for i in chain.from_iterable(constants)] | 256 return [i for i in chain.from_iterable(constants)] |
215 | 257 |
216 def GetAllEnums(module): | 258 def GetAllEnums(module): |
217 data = [module] + module.structs + module.interfaces | 259 data = [module] + module.structs + module.interfaces |
218 enums = [x.enums for x in data] | 260 enums = [x.enums for x in data] |
219 return [i for i in chain.from_iterable(enums)] | 261 return [i for i in chain.from_iterable(enums)] |
220 | 262 |
221 # Adds an import required to use the provided |element|. | 263 # Adds an import required to use the provided |element|. |
222 # The required import is stored at '_imports'. | 264 # The required import is stored at '_imports'. |
265 # The mojom imports are also stored separately in '_mojom_imports'. | |
223 def AddImport(module, element): | 266 def AddImport(module, element): |
224 if not isinstance(element, mojom.Kind): | 267 if not isinstance(element, mojom.Kind): |
225 return | 268 return |
226 | 269 |
227 if mojom.IsArrayKind(element) or mojom.IsInterfaceRequestKind(element): | 270 if mojom.IsArrayKind(element) or mojom.IsInterfaceRequestKind(element): |
228 AddImport(module, element.kind) | 271 AddImport(module, element.kind) |
229 return | 272 return |
230 if mojom.IsMapKind(element): | 273 if mojom.IsMapKind(element): |
231 AddImport(module, element.key_kind) | 274 AddImport(module, element.key_kind) |
232 AddImport(module, element.value_kind) | 275 AddImport(module, element.value_kind) |
233 return | 276 return |
234 if mojom.IsAnyHandleKind(element): | 277 if mojom.IsAnyHandleKind(element): |
235 _imports['mojo/public/go/system'] = 'system' | 278 _imports['mojo/public/go/system'] = 'system' |
236 return | 279 return |
237 | 280 |
238 if not hasattr(element, 'imported_from') or not element.imported_from: | 281 if not hasattr(element, 'imported_from') or not element.imported_from: |
239 return | 282 return |
240 imported = element.imported_from | 283 imported = element.imported_from |
241 if GetPackagePath(imported['module']) == GetPackagePath(module): | 284 if GetPackagePath(imported['module']) == GetPackagePath(module): |
242 return | 285 return |
243 path = GetPackagePath(imported['module']) | 286 path = GetPackagePath(imported['module']) |
244 if path in _imports: | 287 if path in _imports: |
245 return | 288 return |
246 name = GetPackageName(imported['module']) | 289 name = GetPackageName(imported['module']) |
247 while name in _imports.values(): | 290 while name in _imports.values(): # This avoids repeated names. |
248 name += '_' | 291 name += '_' |
249 _imports[path] = name | 292 _imports[path] = name |
293 _mojom_imports[path] = name | |
250 | 294 |
295 # The identifier cache is used by the Type generator to determine if a type has | |
296 # already been generated or not. This prevents over-generation of the same type | |
297 # when it is referred to in multiple ways. | |
298 identifier_cache = {} | |
299 def GetIdentifier(kind): | |
300 # Use the kind's module to determine the package name. | |
301 if hasattr(kind, 'module'): | |
302 package = GetPackageName(kind.module) | |
303 elif mojom.IsInterfaceRequestKind(kind): | |
304 package = GetPackageName(kind.kind.module) | |
305 else: | |
306 return '' | |
307 | |
308 # Most kinds have a name, but those that don't should rely on their spec. | |
309 # Since spec can have : and ? characters, these must be replaced. Since ? is | |
310 # replaced with '', the caller must keep track of optionality on its own. | |
311 package_unique = (kind.name if hasattr(kind, 'name') else kind.spec).replace(' :', '_').replace('?', '') | |
azani
2015/10/20 23:35:46
Line is too long. Maybe break this up into a multi
alexfandrianto
2015/10/21 00:12:46
I used two lines, but that would've worked too.
| |
312 return '%s_%s' % (package, package_unique) | |
313 | |
314 def StoreIdentifier(identifier, cache_name): | |
315 if not cache_name in identifier_cache: | |
316 identifier_cache[cache_name] = {} | |
317 identifier_cache[cache_name][identifier] = True | |
318 return '' | |
319 | |
320 def CheckIdentifier(identifier, cache_name): | |
321 if not cache_name in identifier_cache: | |
322 identifier_cache[cache_name] = {} | |
323 return identifier in identifier_cache[cache_name] | |
324 | |
325 # Get the mojom type's identifier suffix. | |
326 def GetMojomTypeIdentifier(kind): | |
327 # Since this should be unique, it is based on the type's identifier. | |
328 return "%s__" % GetIdentifier(kind) | |
251 | 329 |
252 class Generator(generator.Generator): | 330 class Generator(generator.Generator): |
253 go_filters = { | 331 go_filters = { |
254 'array': lambda kind: mojom.Array(kind), | 332 'array': lambda kind: mojom.Array(kind), |
255 'bit_size': GetBitSize, | 333 'bit_size': GetBitSize, |
256 'decode_suffix': DecodeSuffix, | 334 'decode_suffix': DecodeSuffix, |
257 'encode_suffix': EncodeSuffix, | 335 'encode_suffix': EncodeSuffix, |
258 'go_type': GetGoType, | 336 'go_type': GetGoType, |
259 'expression_to_text': ExpressionToText, | 337 'expression_to_text': ExpressionToText, |
260 'has_response': lambda method: method.response_parameters is not None, | 338 'has_response': lambda method: method.response_parameters is not None, |
339 'identifier': GetIdentifier, | |
340 'identifier_check': CheckIdentifier, | |
341 'identifier_store': StoreIdentifier, | |
261 'is_array': mojom.IsArrayKind, | 342 'is_array': mojom.IsArrayKind, |
262 'is_enum': mojom.IsEnumKind, | 343 'is_enum': mojom.IsEnumKind, |
263 'is_handle': mojom.IsAnyHandleKind, | 344 'is_handle': mojom.IsAnyHandleKind, |
264 'is_interface': mojom.IsInterfaceKind, | 345 'is_interface': mojom.IsInterfaceKind, |
265 'is_interface_request': mojom.IsInterfaceRequestKind, | 346 'is_interface_request': mojom.IsInterfaceRequestKind, |
266 'is_map': mojom.IsMapKind, | 347 'is_map': mojom.IsMapKind, |
267 'is_none_or_empty': lambda array: array is None or len(array) == 0, | 348 'is_none_or_empty': lambda array: array is None or len(array) == 0, |
268 'is_nullable': mojom.IsNullableKind, | 349 'is_nullable': mojom.IsNullableKind, |
269 'is_pointer': IsPointer, | 350 'is_pointer': IsPointer, |
270 'is_object': mojom.IsObjectKind, | 351 'is_object': mojom.IsObjectKind, |
271 'is_struct': mojom.IsStructKind, | 352 'is_struct': mojom.IsStructKind, |
272 'is_union': mojom.IsUnionKind, | 353 'is_union': mojom.IsUnionKind, |
273 'qualified': GetQualifiedName, | 354 'qualified': GetQualifiedName, |
355 'mojom_type': GetMojomTypeValue, | |
356 'mojom_type_identifier': GetMojomTypeIdentifier, | |
274 'name': GetNameForElement, | 357 'name': GetNameForElement, |
275 'unqualified_name': GetUnqualifiedNameForElement, | 358 'unqualified_name': GetUnqualifiedNameForElement, |
276 'package': GetPackageNameForElement, | 359 'package': GetPackageNameForElement, |
277 'tab_indent': lambda s, size = 1: ('\n' + '\t' * size).join(s.splitlines()) | 360 'tab_indent': lambda s, size = 1: ('\n' + '\t' * size).join(s.splitlines()) |
278 } | 361 } |
279 | 362 |
280 def GetParameters(self): | 363 def GetParameters(self): |
364 package = GetPackageName(self.module) | |
281 return { | 365 return { |
282 'enums': GetAllEnums(self.module), | 366 'enums': GetAllEnums(self.module), |
283 'imports': self.GetImports(), | 367 'imports': self.GetImports()[0], |
284 'interfaces': self.GetInterfaces(), | 368 'interfaces': self.GetInterfaces(), |
285 'package': GetPackageName(self.module), | 369 'mojom_imports': self.GetMojomImports(), |
370 'package': package, | |
286 'structs': self.GetStructs(), | 371 'structs': self.GetStructs(), |
287 'unions': self.GetUnions(), | 372 'descpkg': '%s.' % _service_describer_pkg_short if package != _service_des criber_pkg_short else '', |
373 'typepkg': '%s.' % _mojom_types_pkg_short if package != _mojom_types_pkg_s hort else '', | |
374 'unions': self.GetUnions() | |
288 } | 375 } |
289 | 376 |
290 @UseJinja('go_templates/source.tmpl', filters=go_filters) | 377 @UseJinja('go_templates/source.tmpl', filters=go_filters) |
291 def GenerateSource(self): | 378 def GenerateSource(self): |
292 return self.GetParameters() | 379 return self.GetParameters() |
293 | 380 |
294 def GenerateFiles(self, args): | 381 def GenerateFiles(self, args): |
295 self.Write(self.GenerateSource(), os.path.join("go", "src", | 382 self.Write(self.GenerateSource(), os.path.join("go", "src", |
296 GetPackagePath(self.module), "%s.go" % self.module.name)) | 383 GetPackagePath(self.module), "%s.go" % self.module.name)) |
297 | 384 |
298 def GetJinjaParameters(self): | 385 def GetJinjaParameters(self): |
299 return { | 386 return { |
300 'lstrip_blocks': True, | 387 'lstrip_blocks': True, |
301 'trim_blocks': True, | 388 'trim_blocks': True, |
302 } | 389 } |
303 | 390 |
304 def GetGlobals(self): | 391 def GetGlobals(self): |
305 return { | 392 return { |
306 'namespace': self.module.namespace, | 393 'namespace': self.module.namespace, |
307 'module': self.module, | 394 'module': self.module, |
308 } | 395 } |
309 | 396 |
310 # Scans |self.module| for elements that require imports and adds all found | 397 # Scans |self.module| for elements that require imports and adds all found |
311 # imports to '_imports' dict. Returns a list of imports that should include | 398 # imports to '_imports' dict. Mojom imports are stored in the '_mojom_imports' |
312 # the generated go file. | 399 # dict. This operation is idempotent. |
400 # Returns a tuple: | |
401 # - list of imports that should include the generated go file | |
402 # - the dictionary of _mojom_imports | |
313 def GetImports(self): | 403 def GetImports(self): |
314 # Imports can only be used in structs, constants, enums, interfaces. | 404 # Imports can only be used in structs, constants, enums, interfaces. |
315 all_structs = list(self.module.structs) | 405 all_structs = list(self.module.structs) |
316 for i in self.module.interfaces: | 406 for i in self.module.interfaces: |
317 for method in i.methods: | 407 for method in i.methods: |
318 all_structs.append(self._GetStructFromMethod(method)) | 408 all_structs.append(self._GetStructFromMethod(method)) |
319 if method.response_parameters: | 409 if method.response_parameters: |
320 all_structs.append(self._GetResponseStructFromMethod(method)) | 410 all_structs.append(self._GetResponseStructFromMethod(method)) |
321 | 411 |
322 if (len(all_structs) > 0 or len(self.module.interfaces) > 0 | 412 if (len(all_structs) > 0 or len(self.module.interfaces) > 0 |
(...skipping 14 matching lines...) Expand all Loading... | |
337 AddImport(self.module, field.kind) | 427 AddImport(self.module, field.kind) |
338 # TODO(rogulenko): add these after generating constants and struct defaults. | 428 # TODO(rogulenko): add these after generating constants and struct defaults. |
339 # if field.default: | 429 # if field.default: |
340 # AddImport(self.module, field.default) | 430 # AddImport(self.module, field.default) |
341 | 431 |
342 for enum in GetAllEnums(self.module): | 432 for enum in GetAllEnums(self.module): |
343 for field in enum.fields: | 433 for field in enum.fields: |
344 if field.value: | 434 if field.value: |
345 AddImport(self.module, field.value) | 435 AddImport(self.module, field.value) |
346 | 436 |
437 num_user_defined_types = len(self.module.interfaces) + \ | |
438 len(self.module.unions) + len(all_structs) + len(GetAllEnums(self.module)) | |
439 if num_user_defined_types > 0 \ | |
440 and GetPackageName(self.module) != _mojom_types_pkg_short: | |
441 _imports[_mojom_types_pkg] = _mojom_types_pkg_short | |
442 | |
443 if len(self.module.interfaces) > 0 \ | |
444 and GetPackageName(self.module) != _mojom_types_pkg_short \ | |
445 and GetPackageName(self.module) != _service_describer_pkg_short: | |
446 _imports[_service_describer_pkg] = _service_describer_pkg_short | |
447 | |
347 # TODO(rogulenko): add these after generating constants and struct defaults. | 448 # TODO(rogulenko): add these after generating constants and struct defaults. |
348 # for constant in GetAllConstants(self.module): | 449 # for constant in GetAllConstants(self.module): |
349 # AddImport(self.module, constant.value) | 450 # AddImport(self.module, constant.value) |
350 | 451 |
351 imports_list = [] | 452 imports_list = [] |
352 for i in _imports: | 453 for i in _imports: |
353 if i.split('/')[-1] == _imports[i]: | 454 if i.split('/')[-1] == _imports[i]: |
354 imports_list.append('"%s"' % i) | 455 imports_list.append('"%s"' % i) |
355 else: | 456 else: |
356 imports_list.append('%s "%s"' % (_imports[i], i)) | 457 imports_list.append('%s "%s"' % (_imports[i], i)) |
357 return sorted(imports_list) | 458 return sorted(imports_list), _mojom_imports |
459 | |
460 def GetMojomImports(self): | |
461 # GetImports (idempotent) prepares the _imports and _mojom_imports maps. | |
462 return self.GetImports()[1] | |
358 | 463 |
359 # Overrides the implementation from the base class in order to customize the | 464 # Overrides the implementation from the base class in order to customize the |
360 # struct and field names. | 465 # struct and field names. |
361 def _GetStructFromMethod(self, method): | 466 def _GetStructFromMethod(self, method): |
362 params_class = "%s_%s_Params" % (GetNameForElement(method.interface), | 467 params_class = "%s_%s_Params" % (GetNameForElement(method.interface), |
363 GetNameForElement(method)) | 468 GetNameForElement(method)) |
364 struct = mojom.Struct(params_class, module=method.interface.module) | 469 struct = mojom.Struct(params_class, module=method.interface.module) |
365 for param in method.parameters: | 470 for param in method.parameters: |
366 struct.AddField("in%s" % GetNameForElement(param), | 471 struct.AddField("in%s" % GetNameForElement(param), |
367 param.kind, param.ordinal, attributes=param.attributes) | 472 param.kind, param.ordinal, attributes=param.attributes) |
368 return self._AddStructComputedData(False, struct) | 473 return self._AddStructComputedData(False, struct) |
369 | 474 |
370 # Overrides the implementation from the base class in order to customize the | 475 # Overrides the implementation from the base class in order to customize the |
371 # struct and field names. | 476 # struct and field names. |
372 def _GetResponseStructFromMethod(self, method): | 477 def _GetResponseStructFromMethod(self, method): |
373 params_class = "%s_%s_ResponseParams" % ( | 478 params_class = "%s_%s_ResponseParams" % ( |
374 GetNameForElement(method.interface), GetNameForElement(method)) | 479 GetNameForElement(method.interface), GetNameForElement(method)) |
375 struct = mojom.Struct(params_class, module=method.interface.module) | 480 struct = mojom.Struct(params_class, module=method.interface.module) |
376 for param in method.response_parameters: | 481 for param in method.response_parameters: |
377 struct.AddField("out%s" % GetNameForElement(param), | 482 struct.AddField("out%s" % GetNameForElement(param), |
378 param.kind, param.ordinal, attributes=param.attributes) | 483 param.kind, param.ordinal, attributes=param.attributes) |
379 return self._AddStructComputedData(False, struct) | 484 return self._AddStructComputedData(False, struct) |
OLD | NEW |