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 \ |
| 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 Loading... |
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 Loading... |
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) |
OLD | NEW |