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

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

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 4 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 2015 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 Go source files from a mojom.Module.'''
6
7 from itertools import chain
8 import os
9 import re
10
11 from mojom.generate.template_expander import UseJinja
12
13 import mojom.generate.generator as generator
14 import mojom.generate.module as mojom
15 import mojom.generate.pack as pack
16
17 class KindInfo(object):
18 def __init__(self, go_type, encode_suffix, decode_suffix, bit_size):
19 self.go_type = go_type
20 self.encode_suffix = encode_suffix
21 self.decode_suffix = decode_suffix
22 self.bit_size = bit_size
23
24 _kind_infos = {
25 mojom.BOOL: KindInfo('bool', 'Bool', 'Bool', 1),
26 mojom.INT8: KindInfo('int8', 'Int8', 'Int8', 8),
27 mojom.UINT8: KindInfo('uint8', 'Uint8', 'Uint8', 8),
28 mojom.INT16: KindInfo('int16', 'Int16', 'Int16', 16),
29 mojom.UINT16: KindInfo('uint16', 'Uint16', 'Uint16', 16),
30 mojom.INT32: KindInfo('int32', 'Int32', 'Int32', 32),
31 mojom.UINT32: KindInfo('uint32', 'Uint32', 'Uint32', 32),
32 mojom.FLOAT: KindInfo('float32', 'Float32', 'Float32', 32),
33 mojom.HANDLE: KindInfo(
34 'system.Handle', 'Handle', 'Handle', 32),
35 mojom.DCPIPE: KindInfo(
36 'system.ConsumerHandle', 'Handle', 'ConsumerHandle', 32),
37 mojom.DPPIPE: KindInfo(
38 'system.ProducerHandle', 'Handle', 'ProducerHandle', 32),
39 mojom.MSGPIPE: KindInfo(
40 'system.MessagePipeHandle', 'Handle', 'MessagePipeHandle', 32),
41 mojom.SHAREDBUFFER: KindInfo(
42 'system.SharedBufferHandle', 'Handle', 'SharedBufferHandle', 32),
43 mojom.NULLABLE_HANDLE: KindInfo(
44 'system.Handle', 'Handle', 'Handle', 32),
45 mojom.NULLABLE_DCPIPE: KindInfo(
46 'system.ConsumerHandle', 'Handle', 'ConsumerHandle', 32),
47 mojom.NULLABLE_DPPIPE: KindInfo(
48 'system.ProducerHandle', 'Handle', 'ProducerHandle', 32),
49 mojom.NULLABLE_MSGPIPE: KindInfo(
50 'system.MessagePipeHandle', 'Handle', 'MessagePipeHandle', 32),
51 mojom.NULLABLE_SHAREDBUFFER: KindInfo(
52 'system.SharedBufferHandle', 'Handle', 'SharedBufferHandle', 32),
53 mojom.INT64: KindInfo('int64', 'Int64', 'Int64', 64),
54 mojom.UINT64: KindInfo('uint64', 'Uint64', 'Uint64', 64),
55 mojom.DOUBLE: KindInfo('float64', 'Float64', 'Float64', 64),
56 mojom.STRING: KindInfo('string', 'String', 'String', 64),
57 mojom.NULLABLE_STRING: KindInfo('string', 'String', 'String', 64),
58 }
59
60
61 # The mojom_types.mojom and service_describer.mojom files are special because
62 # they are used to generate mojom Type's and ServiceDescription implementations.
63 _service_describer_pkg_short = "service_describer"
64 _service_describer_pkg = "mojo/public/interfaces/bindings/%s" % \
65 _service_describer_pkg_short
66 _mojom_types_pkg_short = "mojom_types"
67 _mojom_types_pkg = "mojo/public/interfaces/bindings/%s" % _mojom_types_pkg_short
68
69 def GetBitSize(kind):
70 if isinstance(kind, (mojom.Union)):
71 return 128
72 if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct, mojom.Interface)):
73 return 64
74 if mojom.IsUnionKind(kind):
75 return 2*64
76 if isinstance(kind, (mojom.InterfaceRequest)):
77 kind = mojom.MSGPIPE
78 if isinstance(kind, mojom.Enum):
79 kind = mojom.INT32
80 return _kind_infos[kind].bit_size
81
82 # Returns go type corresponding to provided kind. If |nullable| is true
83 # and kind is nullable adds an '*' to type (example: ?string -> *string).
84 def GetGoType(kind, nullable = True):
85 if nullable and mojom.IsNullableKind(kind) and not mojom.IsUnionKind(kind):
86 return '*%s' % GetNonNullableGoType(kind)
87 return GetNonNullableGoType(kind)
88
89 # Returns go type corresponding to provided kind. Ignores nullability of
90 # top-level kind.
91 def GetNonNullableGoType(kind):
92 if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
93 return '%s' % GetFullName(kind)
94 if mojom.IsArrayKind(kind):
95 if kind.length:
96 return '[%s]%s' % (kind.length, GetGoType(kind.kind))
97 return '[]%s' % GetGoType(kind.kind)
98 if mojom.IsMapKind(kind):
99 return 'map[%s]%s' % (GetGoType(kind.key_kind), GetGoType(kind.value_kind))
100 if mojom.IsInterfaceKind(kind):
101 return '%s_Pointer' % GetFullName(kind)
102 if mojom.IsInterfaceRequestKind(kind):
103 return '%s_Request' % GetFullName(kind.kind)
104 if mojom.IsEnumKind(kind):
105 return GetNameForNestedElement(kind)
106 return _kind_infos[kind].go_type
107
108 def IsPointer(kind):
109 return mojom.IsObjectKind(kind) and not mojom.IsUnionKind(kind)
110
111 # Splits name to lower-cased parts used for camel-casing
112 # (example: HTTPEntry2FooBar -> ['http', 'entry2', 'foo', 'bar']).
113 def NameToComponent(name):
114 # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
115 # HTTP_Entry2_FooBar)
116 name = re.sub('([^_])([A-Z][^A-Z0-9_]+)', r'\1_\2', name)
117 # insert '_' between non upper and start of upper blocks (e.g.,
118 # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
119 name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
120 return [x.lower() for x in name.split('_')]
121
122 def UpperCamelCase(name):
123 return ''.join([x.capitalize() for x in NameToComponent(name)])
124
125 # Formats a name. If |exported| is true makes name camel-cased with first
126 # letter capital, otherwise does no camel-casing and makes first letter
127 # lower-cased (which is used for making internal names more readable).
128 def FormatName(name, exported=True):
129 if exported:
130 return UpperCamelCase(name)
131 # Leave '_' symbols for unexported names.
132 return name[0].lower() + name[1:]
133
134 # Returns full name of an imported element.
135 # If the |element| is not imported returns formatted name of it.
136 # |element| should have attr 'name'. |exported| argument is used to make
137 # |FormatName()| calls only.
138 def GetFullName(element, exported=True):
139 return GetQualifiedName(
140 element.name, GetPackageNameForElement(element), exported)
141
142 def GetUnqualifiedNameForElement(element, exported=True):
143 return FormatName(element.name, exported)
144
145 # Returns a name for nested elements like enum field or constant.
146 # The returned name consists of camel-cased parts separated by '_'.
147 def GetNameForNestedElement(element):
148 if element.parent_kind:
149 return "%s_%s" % (GetNameForElement(element.parent_kind),
150 FormatName(element.name))
151 return GetFullName(element)
152
153 def GetNameForElement(element, exported=True):
154 if (mojom.IsInterfaceKind(element) or mojom.IsStructKind(element)
155 or mojom.IsUnionKind(element)):
156 return GetFullName(element, exported)
157 if isinstance(element, (mojom.EnumField,
158 mojom.Field,
159 mojom.Method,
160 mojom.Parameter)):
161 element_name = (element.go_name if hasattr(element, "go_name")
162 else element.name)
163 return FormatName(element_name, exported)
164 if isinstance(element, (mojom.Enum,
165 mojom.Constant,
166 mojom.ConstantValue)):
167 return GetNameForNestedElement(element)
168 raise Exception('Unexpected element: %s' % element)
169
170 def ExpressionToText(token):
171 if isinstance(token, mojom.EnumValue):
172 return "%s_%s" % (GetNameForNestedElement(token.enum),
173 FormatName(token.name, True))
174 if isinstance(token, mojom.ConstantValue):
175 return GetNameForNestedElement(token)
176 if isinstance(token, mojom.Constant):
177 return ExpressionToText(token.value)
178 return token
179
180 def DecodeSuffix(kind):
181 if mojom.IsEnumKind(kind):
182 return DecodeSuffix(mojom.INT32)
183 if mojom.IsInterfaceKind(kind):
184 return 'Interface'
185 if mojom.IsInterfaceRequestKind(kind):
186 return DecodeSuffix(mojom.MSGPIPE)
187 return _kind_infos[kind].decode_suffix
188
189 def EncodeSuffix(kind):
190 if mojom.IsEnumKind(kind):
191 return EncodeSuffix(mojom.INT32)
192 if mojom.IsInterfaceKind(kind):
193 return 'Interface'
194 if mojom.IsInterfaceRequestKind(kind):
195 return EncodeSuffix(mojom.MSGPIPE)
196 return _kind_infos[kind].encode_suffix
197
198 def GetPackageName(module):
199 return module.name.split('.')[0]
200
201 def GetPackageNameForElement(element):
202 if not hasattr(element, 'imported_from') or not element.imported_from:
203 return ''
204 return element.imported_from.get('go_name', '')
205
206 def GetTypeKeyForElement(element):
207 if not hasattr(element, 'type_key') or not element.type_key:
208 return ''
209 return element.type_key
210
211 def GetQualifiedName(name, package=None, exported=True):
212 if not package:
213 return FormatName(name, exported)
214 return '%s.%s' % (package, FormatName(name, exported))
215
216 def GetPackagePath(module):
217 name = module.name.split('.')[0]
218 return '/'.join(module.path.split('/')[:-1] + [name])
219
220 def GetAllConstants(module):
221 data = [module] + module.structs + module.interfaces
222 constants = [x.constants for x in data]
223 return [i for i in chain.from_iterable(constants)]
224
225 def GetAllEnums(module):
226 data = [module] + module.structs + module.interfaces
227 enums = [x.enums for x in data]
228 return [i for i in chain.from_iterable(enums)]
229
230 def AddImport(imports, mojom_imports, module, element):
231 """Adds an import required to use the provided element.
232
233 The required import is stored in the imports parameter.
234 The corresponding mojom import is stored in the mojom_imports parameter.
235 Each import is also updated to include a 'go_name' entry. The 'go_name' entry
236 is the name by which the imported module will be referred to in the generated
237 code. Because the import dictionary is accessible from the element's
238 imported_from field this allows us to generate the qualified name for the
239 element.
240
241 Args:
242 imports: {dict<str, str>} The key is the path to the import and the value
243 is the go name.
244 mojom_imports: {dict<str, str>} The key is the path to the import and the
245 value is the go name.
246 module: {module.Module} the module being processed.
247 element: {module.Kind} the element whose import is to be tracked.
248 """
249 if not isinstance(element, mojom.Kind):
250 return
251
252 if mojom.IsArrayKind(element) or mojom.IsInterfaceRequestKind(element):
253 AddImport(imports, mojom_imports, module, element.kind)
254 return
255 if mojom.IsMapKind(element):
256 AddImport(imports, mojom_imports, module, element.key_kind)
257 AddImport(imports, mojom_imports, module, element.value_kind)
258 return
259 if mojom.IsAnyHandleKind(element):
260 imports['mojo/public/go/system'] = 'system'
261 return
262
263 if not hasattr(element, 'imported_from') or not element.imported_from:
264 return
265 imported = element.imported_from
266 if GetPackagePath(imported['module']) == GetPackagePath(module):
267 return
268 path = GetPackagePath(imported['module'])
269 if path in imports:
270 return
271 name = GetPackageName(imported['module'])
272 while name in imports.values(): # This avoids repeated names.
273 name += '_'
274 imported['go_name'] = name
275 imports[path] = name
276 mojom_imports[path] = name
277
278 class Generator(generator.Generator):
279 go_filters = {
280 'array': lambda kind: mojom.Array(kind),
281 'bit_size': GetBitSize,
282 'decode_suffix': DecodeSuffix,
283 'encode_suffix': EncodeSuffix,
284 'go_type': GetGoType,
285 'expression_to_text': ExpressionToText,
286 'has_response': lambda method: method.response_parameters is not None,
287 'is_array': mojom.IsArrayKind,
288 'is_enum': mojom.IsEnumKind,
289 'is_handle': mojom.IsAnyHandleKind,
290 'is_interface': mojom.IsInterfaceKind,
291 'is_interface_request': mojom.IsInterfaceRequestKind,
292 'is_map': mojom.IsMapKind,
293 'is_none_or_empty': lambda array: array is None or len(array) == 0,
294 'is_nullable': mojom.IsNullableKind,
295 'is_pointer': IsPointer,
296 'is_object': mojom.IsObjectKind,
297 'is_struct': mojom.IsStructKind,
298 'is_union': mojom.IsUnionKind,
299 'qualified': GetQualifiedName,
300 'mojom_type_key' : GetTypeKeyForElement,
301 'name': GetNameForElement,
302 'unqualified_name': GetUnqualifiedNameForElement,
303 'package': GetPackageNameForElement,
304 'tab_indent': lambda s, size = 1: ('\n' + '\t' * size).join(s.splitlines())
305 }
306
307 # If set to True, then mojom type information will be generated.
308 should_gen_mojom_types = False
309
310 def GetParameters(self):
311 package = GetPackageName(self.module)
312 imports, mojom_imports = self.GetImports()
313 return {
314 'enums': GetAllEnums(self.module),
315 'imports': imports,
316 'interfaces': self.GetInterfaces(),
317 'mojom_imports': mojom_imports,
318 'package': package,
319 'structs': self.GetStructs(),
320 'descpkg': '%s.' % _service_describer_pkg_short \
321 if package != _service_describer_pkg_short else '',
322 'typepkg': '%s.' % _mojom_types_pkg_short \
323 if package != _mojom_types_pkg_short else '',
324 'unions': self.GetUnions(),
325 }
326
327 @UseJinja('go_templates/source.tmpl', filters=go_filters)
328 def GenerateSource(self):
329 return self.GetParameters()
330
331 def GenerateFiles(self, args):
332 self.should_gen_mojom_types = "--generate_type_info" in args
333
334 self.Write(self.GenerateSource(), os.path.join("go", "src",
335 GetPackagePath(self.module), "%s.go" % self.module.name))
336
337 def GetJinjaParameters(self):
338 return {
339 'lstrip_blocks': True,
340 'trim_blocks': True,
341 }
342
343 def GetGlobals(self):
344 return {
345 'namespace': self.module.namespace,
346 'module': self.module,
347 'should_gen_mojom_types': self.should_gen_mojom_types,
348 }
349
350 def GetImports(self):
351 """Gets the current module's imports.
352
353 Returns:
354 tuple(dict<str, str>, dict<str, str>)
355 The first element of the tuple is a dictionary mapping import paths to
356 import names.
357 The second element is a dictionary mapping import paths to import names
358 only for imported mojom files.
359 """
360 imports = {}
361 mojom_imports = {}
362 # Imports are referred to by the imported_from field of imported kinds.
363 # Imported kinds can only be referred to in structs, constants, enums,
364 # unions and interfaces.
365 all_structs = list(self.module.structs)
366 for i in self.module.interfaces:
367 for method in i.methods:
368 all_structs.append(self._GetStructFromMethod(method))
369 if method.response_parameters:
370 all_structs.append(self._GetResponseStructFromMethod(method))
371
372 if (len(all_structs) > 0 or len(self.module.interfaces) > 0
373 or len(self.module.unions) > 0):
374 imports['fmt'] = 'fmt'
375 imports['mojo/public/go/bindings'] = 'bindings'
376 if len(self.module.interfaces) > 0:
377 imports['mojo/public/go/system'] = 'system'
378 if len(all_structs) > 0:
379 imports['sort'] = 'sort'
380
381 for union in self.module.unions:
382 for field in union.fields:
383 AddImport(imports, mojom_imports, self.module, field.kind)
384
385 for struct in all_structs:
386 for field in struct.fields:
387 AddImport(imports, mojom_imports, self.module, field.kind)
388 # TODO(rogulenko): add these after generating constants and struct defaults.
389 # if field.default:
390 # AddImport(imports, mojom_imports, self.module, field.default)
391
392 for enum in GetAllEnums(self.module):
393 for field in enum.fields:
394 if field.value:
395 AddImport(imports, mojom_imports, self.module, field.value)
396
397 # Mojom Type generation requires additional imports.
398 defInterface = len(self.module.interfaces) > 0
399 defOtherType = len(self.module.unions) + len(all_structs) + \
400 len(GetAllEnums(self.module)) > 0
401
402 if self.should_gen_mojom_types:
403 imports['bytes'] = 'bytes'
404 imports['compress/gzip'] = 'gzip'
405 imports['encoding/base64'] = 'base64'
406 imports['fmt'] = 'fmt'
407 imports['io/ioutil'] = 'ioutil'
408 imports['mojo/public/go/bindings'] = 'bindings'
409
410 if GetPackageName(self.module) != _mojom_types_pkg_short:
411 if defInterface:
412 # Each Interface has a service description that uses this.
413 imports[_mojom_types_pkg] = _mojom_types_pkg_short
414 if defOtherType and self.should_gen_mojom_types:
415 # This import is needed only if generating mojom type definitions.
416 imports[_mojom_types_pkg] = _mojom_types_pkg_short
417
418 if GetPackageName(self.module) != _service_describer_pkg_short and \
419 defInterface:
420 # Each Interface has a service description that uses this.
421 imports[_service_describer_pkg] = _service_describer_pkg_short
422
423 # TODO(rogulenko): add these after generating constants and struct defaults.
424 # for constant in GetAllConstants(self.module):
425 # AddImport(imports, mojom_imports, self.module, constant.value)
426
427 return imports, mojom_imports
428
429 # Overrides the implementation from the base class in order to customize the
430 # struct and field names. Since the Python objects representing the struct
431 # and fields are shared by all language generators we don't want to actually
432 # modify the |name| property. Instead we add a |go_name| property.
433 def _GetStructFromMethod(self, method):
434 self._AddStructComputedData(False, method.param_struct)
435 # Only generate the go_names if they have not already been generated.
436 if not hasattr(method.param_struct, "go_name"):
437 method.param_struct.go_name = "%s_%s_Params" % (
438 GetNameForElement(method.interface), GetNameForElement(method))
439 for field in method.param_struct.fields:
440 field.go_name = "in%s" % GetNameForElement(field)
441 return method.param_struct
442
443 # Overrides the implementation from the base class in order to customize the
444 # struct and field names. Since the Python objects representing the struct
445 # and fields are shared by all language generators we don't want to actually
446 # modify the |name| property. Instead we add a |go_name| property.
447 def _GetResponseStructFromMethod(self, method):
448 self._AddStructComputedData(False, method.response_param_struct)
449 if not hasattr(method.response_param_struct, "go_name"):
450 # Only generate the go_names if they have not already been generated.
451 method.response_param_struct.go_name = "%s_%s_ResponseParams" % (
452 GetNameForElement(method.interface), GetNameForElement(method))
453 for field in method.response_param_struct.fields:
454 field.go_name = "out%s" % GetNameForElement(field)
455 return method.response_param_struct
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698