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

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

Issue 1345263002: Generate Mojom Types in Go (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Address Naming changes and Comment Updates Created 5 years, 1 month 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
« no previous file with comments | « mojo/public/tools/bindings/generators/go_templates/union.tmpl ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 \
209 mojom.IsIntegralKind(kind):
210
211 kind_name = UpperCamelCase(_kind_infos[kind].decode_suffix.upper())
212 if kind == mojom.FLOAT:
213 kind_name = "Float"
214 elif kind == mojom.DOUBLE:
215 kind_name = "Double"
216 return '%sTypeSimpleType{%sSimpleType_%s}' % (typepkg, typepkg, kind_name)
217 elif mojom.IsAnyHandleKind(kind):
218 kind_name = 'Unspecified'
219 if kind == mojom.DCPIPE:
220 kind_name = 'DataPipeConsumer'
221 elif kind == mojom.DPPIPE:
222 kind_name = 'DataPipeProducer'
223 elif kind == mojom.MSGPIPE:
224 kind_name = 'MessagePipe'
225 elif kind == mojom.SHAREDBUFFER:
226 kind_name = 'SharedBuffer'
227 return '%sTypeHandleType{%sHandleType{' \
228 'Nullable: %s, Kind: %sHandleType_Kind_%s}}' % \
229 (typepkg, typepkg, nullable, typepkg, kind_name)
230 elif mojom.IsStringKind(kind):
231 return '%sTypeStringType{%sStringType{%s}}' % (typepkg, typepkg, nullable)
232 else:
233 raise Exception('Missing case for kind: %s' % kind)
234
189 def GetPackageName(module): 235 def GetPackageName(module):
190 return module.name.split('.')[0] 236 return module.name.split('.')[0]
191 237
192 def GetPackageNameForElement(element): 238 def GetPackageNameForElement(element):
193 if not hasattr(element, 'imported_from') or not element.imported_from: 239 if not hasattr(element, 'imported_from') or not element.imported_from:
194 return '' 240 return ''
195 path = '' 241 path = ''
196 if element.imported_from['module'].path: 242 if element.imported_from['module'].path:
197 path += GetPackagePath(element.imported_from['module']) 243 path += GetPackagePath(element.imported_from['module'])
198 if path in _imports: 244 if path in _imports:
(...skipping 14 matching lines...) Expand all
213 constants = [x.constants for x in data] 259 constants = [x.constants for x in data]
214 return [i for i in chain.from_iterable(constants)] 260 return [i for i in chain.from_iterable(constants)]
215 261
216 def GetAllEnums(module): 262 def GetAllEnums(module):
217 data = [module] + module.structs + module.interfaces 263 data = [module] + module.structs + module.interfaces
218 enums = [x.enums for x in data] 264 enums = [x.enums for x in data]
219 return [i for i in chain.from_iterable(enums)] 265 return [i for i in chain.from_iterable(enums)]
220 266
221 # Adds an import required to use the provided |element|. 267 # Adds an import required to use the provided |element|.
222 # The required import is stored at '_imports'. 268 # The required import is stored at '_imports'.
269 # The mojom imports are also stored separately in '_mojom_imports'.
223 def AddImport(module, element): 270 def AddImport(module, element):
224 if not isinstance(element, mojom.Kind): 271 if not isinstance(element, mojom.Kind):
225 return 272 return
226 273
227 if mojom.IsArrayKind(element) or mojom.IsInterfaceRequestKind(element): 274 if mojom.IsArrayKind(element) or mojom.IsInterfaceRequestKind(element):
228 AddImport(module, element.kind) 275 AddImport(module, element.kind)
229 return 276 return
230 if mojom.IsMapKind(element): 277 if mojom.IsMapKind(element):
231 AddImport(module, element.key_kind) 278 AddImport(module, element.key_kind)
232 AddImport(module, element.value_kind) 279 AddImport(module, element.value_kind)
233 return 280 return
234 if mojom.IsAnyHandleKind(element): 281 if mojom.IsAnyHandleKind(element):
235 _imports['mojo/public/go/system'] = 'system' 282 _imports['mojo/public/go/system'] = 'system'
236 return 283 return
237 284
238 if not hasattr(element, 'imported_from') or not element.imported_from: 285 if not hasattr(element, 'imported_from') or not element.imported_from:
239 return 286 return
240 imported = element.imported_from 287 imported = element.imported_from
241 if GetPackagePath(imported['module']) == GetPackagePath(module): 288 if GetPackagePath(imported['module']) == GetPackagePath(module):
242 return 289 return
243 path = GetPackagePath(imported['module']) 290 path = GetPackagePath(imported['module'])
244 if path in _imports: 291 if path in _imports:
245 return 292 return
246 name = GetPackageName(imported['module']) 293 name = GetPackageName(imported['module'])
247 while name in _imports.values(): 294 while name in _imports.values(): # This avoids repeated names.
248 name += '_' 295 name += '_'
249 _imports[path] = name 296 _imports[path] = name
297 _mojom_imports[path] = name
250 298
299 # The identifier cache is used by the Type generator to determine if a type has
300 # already been generated or not. This prevents over-generation of the same type
301 # when it is referred to in multiple ways.
302 identifier_cache = {}
303 def GetIdentifier(kind):
304 # Use the kind's module to determine the package name.
305 if hasattr(kind, 'module'):
306 package = GetPackageName(kind.module)
307 elif mojom.IsInterfaceRequestKind(kind):
308 package = GetPackageName(kind.kind.module)
309 else:
310 return ''
311
312 # Most kinds have a name, but those that don't should rely on their spec.
313 # Since spec can have : and ? characters, these must be replaced. Since ? is
314 # replaced with '', the caller must keep track of optionality on its own.
315 name_or_spec = (kind.name if hasattr(kind, 'name') else kind.spec)
316 package_unique = name_or_spec.replace(':', '_').replace('?', '')
317 return '%s_%s' % (package, package_unique)
318
319 def StoreIdentifier(identifier, cache_name):
320 if not cache_name in identifier_cache:
321 identifier_cache[cache_name] = {}
322 identifier_cache[cache_name][identifier] = True
323 return ''
324
325 def CheckIdentifier(identifier, cache_name):
326 if not cache_name in identifier_cache:
327 identifier_cache[cache_name] = {}
328 return identifier in identifier_cache[cache_name]
329
330 # Get the mojom type's identifier suffix.
331 def GetMojomTypeIdentifier(kind):
332 # Since this should be unique, it is based on the type's identifier.
333 return "%s__" % GetIdentifier(kind)
251 334
252 class Generator(generator.Generator): 335 class Generator(generator.Generator):
253 go_filters = { 336 go_filters = {
254 'array': lambda kind: mojom.Array(kind), 337 'array': lambda kind: mojom.Array(kind),
255 'bit_size': GetBitSize, 338 'bit_size': GetBitSize,
256 'decode_suffix': DecodeSuffix, 339 'decode_suffix': DecodeSuffix,
257 'encode_suffix': EncodeSuffix, 340 'encode_suffix': EncodeSuffix,
258 'go_type': GetGoType, 341 'go_type': GetGoType,
259 'expression_to_text': ExpressionToText, 342 'expression_to_text': ExpressionToText,
260 'has_response': lambda method: method.response_parameters is not None, 343 'has_response': lambda method: method.response_parameters is not None,
344 'identifier': GetIdentifier,
345 'identifier_check': CheckIdentifier,
346 'identifier_store': StoreIdentifier,
261 'is_array': mojom.IsArrayKind, 347 'is_array': mojom.IsArrayKind,
262 'is_enum': mojom.IsEnumKind, 348 'is_enum': mojom.IsEnumKind,
263 'is_handle': mojom.IsAnyHandleKind, 349 'is_handle': mojom.IsAnyHandleKind,
264 'is_interface': mojom.IsInterfaceKind, 350 'is_interface': mojom.IsInterfaceKind,
265 'is_interface_request': mojom.IsInterfaceRequestKind, 351 'is_interface_request': mojom.IsInterfaceRequestKind,
266 'is_map': mojom.IsMapKind, 352 'is_map': mojom.IsMapKind,
267 'is_none_or_empty': lambda array: array is None or len(array) == 0, 353 'is_none_or_empty': lambda array: array is None or len(array) == 0,
268 'is_nullable': mojom.IsNullableKind, 354 'is_nullable': mojom.IsNullableKind,
269 'is_pointer': IsPointer, 355 'is_pointer': IsPointer,
270 'is_object': mojom.IsObjectKind, 356 'is_object': mojom.IsObjectKind,
271 'is_struct': mojom.IsStructKind, 357 'is_struct': mojom.IsStructKind,
272 'is_union': mojom.IsUnionKind, 358 'is_union': mojom.IsUnionKind,
273 'qualified': GetQualifiedName, 359 'qualified': GetQualifiedName,
360 'mojom_type': GetMojomTypeValue,
361 'mojom_type_identifier': GetMojomTypeIdentifier,
274 'name': GetNameForElement, 362 'name': GetNameForElement,
275 'unqualified_name': GetUnqualifiedNameForElement, 363 'unqualified_name': GetUnqualifiedNameForElement,
276 'package': GetPackageNameForElement, 364 'package': GetPackageNameForElement,
277 'tab_indent': lambda s, size = 1: ('\n' + '\t' * size).join(s.splitlines()) 365 'tab_indent': lambda s, size = 1: ('\n' + '\t' * size).join(s.splitlines())
278 } 366 }
279 367
368 # TODO: This value should be settable via arguments. If False, then mojom type
369 # information will not be generated.
370 should_gen_mojom_types = True
371
280 def GetParameters(self): 372 def GetParameters(self):
373 package = GetPackageName(self.module)
281 return { 374 return {
282 'enums': GetAllEnums(self.module), 375 'enums': GetAllEnums(self.module),
283 'imports': self.GetImports(), 376 'imports': self.GetImports()[0],
284 'interfaces': self.GetInterfaces(), 377 'interfaces': self.GetInterfaces(),
285 'package': GetPackageName(self.module), 378 'mojom_imports': self.GetMojomImports(),
379 'package': package,
286 'structs': self.GetStructs(), 380 'structs': self.GetStructs(),
287 'unions': self.GetUnions(), 381 'descpkg': '%s.' % _service_describer_pkg_short \
382 if package != _service_describer_pkg_short else '',
383 'typepkg': '%s.' % _mojom_types_pkg_short \
384 if package != _mojom_types_pkg_short else '',
385 'unions': self.GetUnions()
288 } 386 }
289 387
290 @UseJinja('go_templates/source.tmpl', filters=go_filters) 388 @UseJinja('go_templates/source.tmpl', filters=go_filters)
291 def GenerateSource(self): 389 def GenerateSource(self):
292 return self.GetParameters() 390 return self.GetParameters()
293 391
294 def GenerateFiles(self, args): 392 def GenerateFiles(self, args):
295 self.Write(self.GenerateSource(), os.path.join("go", "src", 393 self.Write(self.GenerateSource(), os.path.join("go", "src",
296 GetPackagePath(self.module), "%s.go" % self.module.name)) 394 GetPackagePath(self.module), "%s.go" % self.module.name))
297 395
298 def GetJinjaParameters(self): 396 def GetJinjaParameters(self):
299 return { 397 return {
300 'lstrip_blocks': True, 398 'lstrip_blocks': True,
301 'trim_blocks': True, 399 'trim_blocks': True,
302 } 400 }
303 401
304 def GetGlobals(self): 402 def GetGlobals(self):
305 return { 403 return {
306 'namespace': self.module.namespace, 404 'namespace': self.module.namespace,
307 'module': self.module, 405 'module': self.module,
406 'should_gen_mojom_types': self.should_gen_mojom_types,
308 } 407 }
309 408
310 # Scans |self.module| for elements that require imports and adds all found 409 # 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 410 # imports to '_imports' dict. Mojom imports are stored in the '_mojom_imports'
312 # the generated go file. 411 # dict. This operation is idempotent.
412 # Returns a tuple:
413 # - list of imports that should include the generated go file
414 # - the dictionary of _mojom_imports
313 def GetImports(self): 415 def GetImports(self):
314 # Imports can only be used in structs, constants, enums, interfaces. 416 # Imports can only be used in structs, constants, enums, interfaces.
315 all_structs = list(self.module.structs) 417 all_structs = list(self.module.structs)
316 for i in self.module.interfaces: 418 for i in self.module.interfaces:
317 for method in i.methods: 419 for method in i.methods:
318 all_structs.append(self._GetStructFromMethod(method)) 420 all_structs.append(self._GetStructFromMethod(method))
319 if method.response_parameters: 421 if method.response_parameters:
320 all_structs.append(self._GetResponseStructFromMethod(method)) 422 all_structs.append(self._GetResponseStructFromMethod(method))
321 423
322 if (len(all_structs) > 0 or len(self.module.interfaces) > 0 424 if (len(all_structs) > 0 or len(self.module.interfaces) > 0
(...skipping 14 matching lines...) Expand all
337 AddImport(self.module, field.kind) 439 AddImport(self.module, field.kind)
338 # TODO(rogulenko): add these after generating constants and struct defaults. 440 # TODO(rogulenko): add these after generating constants and struct defaults.
339 # if field.default: 441 # if field.default:
340 # AddImport(self.module, field.default) 442 # AddImport(self.module, field.default)
341 443
342 for enum in GetAllEnums(self.module): 444 for enum in GetAllEnums(self.module):
343 for field in enum.fields: 445 for field in enum.fields:
344 if field.value: 446 if field.value:
345 AddImport(self.module, field.value) 447 AddImport(self.module, field.value)
346 448
449 # Mojom Type generation requires additional imports.
450 defInterface = len(self.module.interfaces) > 0
451 defOtherType = len(self.module.unions) + len(all_structs) + \
452 len(GetAllEnums(self.module)) > 0
453
454 if GetPackageName(self.module) != _mojom_types_pkg_short:
455 if defInterface:
456 # Each Interface has a service description that uses this.
457 _imports[_mojom_types_pkg] = _mojom_types_pkg_short
458 if defOtherType and self.should_gen_mojom_types:
459 # This import is needed only if generating mojom type definitions.
460 _imports[_mojom_types_pkg] = _mojom_types_pkg_short
461
462 if GetPackageName(self.module) != _service_describer_pkg_short and \
463 defInterface:
464 # Each Interface has a service description that uses this.
465 _imports[_service_describer_pkg] = _service_describer_pkg_short
466
347 # TODO(rogulenko): add these after generating constants and struct defaults. 467 # TODO(rogulenko): add these after generating constants and struct defaults.
348 # for constant in GetAllConstants(self.module): 468 # for constant in GetAllConstants(self.module):
349 # AddImport(self.module, constant.value) 469 # AddImport(self.module, constant.value)
350 470
351 imports_list = [] 471 imports_list = []
352 for i in _imports: 472 for i in _imports:
353 if i.split('/')[-1] == _imports[i]: 473 if i.split('/')[-1] == _imports[i]:
354 imports_list.append('"%s"' % i) 474 imports_list.append('"%s"' % i)
355 else: 475 else:
356 imports_list.append('%s "%s"' % (_imports[i], i)) 476 imports_list.append('%s "%s"' % (_imports[i], i))
357 return sorted(imports_list) 477 return sorted(imports_list), _mojom_imports
478
479 def GetMojomImports(self):
480 # GetImports (idempotent) prepares the _imports and _mojom_imports maps.
481 return self.GetImports()[1]
358 482
359 # Overrides the implementation from the base class in order to customize the 483 # Overrides the implementation from the base class in order to customize the
360 # struct and field names. 484 # struct and field names.
361 def _GetStructFromMethod(self, method): 485 def _GetStructFromMethod(self, method):
362 params_class = "%s_%s_Params" % (GetNameForElement(method.interface), 486 params_class = "%s_%s_Params" % (GetNameForElement(method.interface),
363 GetNameForElement(method)) 487 GetNameForElement(method))
364 struct = mojom.Struct(params_class, module=method.interface.module) 488 struct = mojom.Struct(params_class, module=method.interface.module)
365 for param in method.parameters: 489 for param in method.parameters:
366 struct.AddField("in%s" % GetNameForElement(param), 490 struct.AddField("in%s" % GetNameForElement(param),
367 param.kind, param.ordinal, attributes=param.attributes) 491 param.kind, param.ordinal, attributes=param.attributes)
368 return self._AddStructComputedData(False, struct) 492 return self._AddStructComputedData(False, struct)
369 493
370 # Overrides the implementation from the base class in order to customize the 494 # Overrides the implementation from the base class in order to customize the
371 # struct and field names. 495 # struct and field names.
372 def _GetResponseStructFromMethod(self, method): 496 def _GetResponseStructFromMethod(self, method):
373 params_class = "%s_%s_ResponseParams" % ( 497 params_class = "%s_%s_ResponseParams" % (
374 GetNameForElement(method.interface), GetNameForElement(method)) 498 GetNameForElement(method.interface), GetNameForElement(method))
375 struct = mojom.Struct(params_class, module=method.interface.module) 499 struct = mojom.Struct(params_class, module=method.interface.module)
376 for param in method.response_parameters: 500 for param in method.response_parameters:
377 struct.AddField("out%s" % GetNameForElement(param), 501 struct.AddField("out%s" % GetNameForElement(param),
378 param.kind, param.ordinal, attributes=param.attributes) 502 param.kind, param.ordinal, attributes=param.attributes)
379 return self._AddStructComputedData(False, struct) 503 return self._AddStructComputedData(False, struct)
OLDNEW
« no previous file with comments | « mojo/public/tools/bindings/generators/go_templates/union.tmpl ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698