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

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: Update Comments and Enum Template+Tests Created 5 years, 2 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
« 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 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
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
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)
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