• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 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 C++ source files from a mojom.Module."""
6
7import mojom.generate.generator as generator
8import mojom.generate.module as mojom
9import mojom.generate.pack as pack
10from mojom.generate.template_expander import UseJinja
11
12
13_kind_to_cpp_type = {
14  mojom.BOOL:                  "bool",
15  mojom.INT8:                  "int8_t",
16  mojom.UINT8:                 "uint8_t",
17  mojom.INT16:                 "int16_t",
18  mojom.UINT16:                "uint16_t",
19  mojom.INT32:                 "int32_t",
20  mojom.UINT32:                "uint32_t",
21  mojom.FLOAT:                 "float",
22  mojom.INT64:                 "int64_t",
23  mojom.UINT64:                "uint64_t",
24  mojom.DOUBLE:                "double",
25}
26
27_kind_to_cpp_literal_suffix = {
28  mojom.UINT8:        "U",
29  mojom.UINT16:       "U",
30  mojom.UINT32:       "U",
31  mojom.FLOAT:        "f",
32  mojom.UINT64:       "ULL",
33}
34
35# TODO(rockot): Get rid of these globals. This requires some refactoring of the
36# generator library code so that filters can use the generator as context.
37_current_typemap = {}
38_for_blink = False
39_use_new_wrapper_types = False
40# TODO(rockot, yzshen): The variant handling is kind of a hack currently. Make
41# it right.
42_variant = None
43
44
45class _NameFormatter(object):
46  """A formatter for the names of kinds or values."""
47
48  def __init__(self, token, variant):
49    self._token = token
50    self._variant = variant
51
52  def Format(self, separator, prefixed=False, internal=False,
53             include_variant=False, add_same_module_namespaces=False):
54    parts = []
55    if self._ShouldIncludeNamespace(add_same_module_namespaces):
56      if prefixed:
57        parts.append("")
58      parts.extend(self._GetNamespace())
59      if include_variant and self._variant:
60        parts.append(self._variant)
61    parts.extend(self._GetName(internal))
62    return separator.join(parts)
63
64  def FormatForCpp(self, add_same_module_namespaces=False, internal=False):
65    return self.Format(
66        "::", prefixed=True,
67        add_same_module_namespaces=add_same_module_namespaces,
68        internal=internal, include_variant=True)
69
70  def FormatForMojom(self):
71    return self.Format(".", add_same_module_namespaces=True)
72
73  def _MapKindName(self, token, internal):
74    if not internal:
75      return token.name
76    if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or
77        mojom.IsInterfaceKind(token) or mojom.IsEnumKind(token)):
78      return token.name + "_Data"
79    return token.name
80
81  def _GetName(self, internal):
82    name = []
83    if internal:
84      name.append("internal")
85    if self._token.parent_kind:
86      name.append(self._MapKindName(self._token.parent_kind, internal))
87    # Both variable and enum constants are constructed like:
88    # Namespace::Struct::CONSTANT_NAME
89    # For enums, CONSTANT_NAME is EnumName::ENUM_VALUE.
90    if isinstance(self._token, mojom.EnumValue):
91      name.extend([self._token.enum.name, self._token.name])
92    else:
93      name.append(self._MapKindName(self._token, internal))
94    return name
95
96  def _ShouldIncludeNamespace(self, add_same_module_namespaces):
97    return add_same_module_namespaces or self._token.imported_from
98
99  def _GetNamespace(self):
100    if self._token.imported_from:
101      return NamespaceToArray(self._token.imported_from["namespace"])
102    elif hasattr(self._token, "module"):
103      return NamespaceToArray(self._token.module.namespace)
104    return []
105
106
107def ConstantValue(constant):
108  return ExpressionToText(constant.value, kind=constant.kind)
109
110# TODO(yzshen): Revisit the default value feature. It was designed prior to
111# custom type mapping.
112def DefaultValue(field):
113  if field.default:
114    if mojom.IsStructKind(field.kind):
115      assert field.default == "default"
116      if not IsTypemappedKind(field.kind):
117        return "%s::New()" % GetNameForKind(field.kind)
118    return ExpressionToText(field.default, kind=field.kind)
119  if not _use_new_wrapper_types:
120    if mojom.IsArrayKind(field.kind) or mojom.IsMapKind(field.kind):
121      return "nullptr";
122    if mojom.IsStringKind(field.kind):
123      return "" if _for_blink else "nullptr"
124  return ""
125
126def NamespaceToArray(namespace):
127  return namespace.split(".") if namespace else []
128
129def GetNameForKind(kind, internal=False):
130  return _NameFormatter(kind, _variant).FormatForCpp(internal=internal)
131
132def GetQualifiedNameForKind(kind, internal=False):
133  return _NameFormatter(kind, _variant).FormatForCpp(
134      internal=internal, add_same_module_namespaces=True)
135
136def GetFullMojomNameForKind(kind):
137  return _NameFormatter(kind, _variant).FormatForMojom()
138
139def IsTypemappedKind(kind):
140  return hasattr(kind, "name") and \
141      GetFullMojomNameForKind(kind) in _current_typemap
142
143def IsNativeOnlyKind(kind):
144  return (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)) and \
145      kind.native_only
146
147def GetNativeTypeName(typemapped_kind):
148  return _current_typemap[GetFullMojomNameForKind(typemapped_kind)]["typename"]
149
150def GetCppPodType(kind):
151  if mojom.IsStringKind(kind):
152    return "char*"
153  return _kind_to_cpp_type[kind]
154
155def GetCppWrapperType(kind):
156  if IsTypemappedKind(kind):
157    return GetNativeTypeName(kind)
158  if mojom.IsEnumKind(kind):
159    return GetNameForKind(kind)
160  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
161    return "%sPtr" % GetNameForKind(kind)
162  if mojom.IsArrayKind(kind):
163    pattern = None
164    if _use_new_wrapper_types:
165      if mojom.IsNullableKind(kind):
166        pattern = ("WTF::Optional<WTF::Vector<%s>>" if _for_blink else
167            "base::Optional<std::vector<%s>>")
168      else:
169        pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>"
170    else:
171      pattern = "mojo::WTFArray<%s>" if _for_blink else "mojo::Array<%s>"
172    return pattern % GetCppWrapperType(kind.kind)
173  if mojom.IsMapKind(kind):
174    pattern = None
175    if _use_new_wrapper_types:
176      if mojom.IsNullableKind(kind):
177        pattern = ("WTF::Optional<WTF::HashMap<%s, %s>>" if _for_blink else
178                   "base::Optional<std::unordered_map<%s, %s>>")
179      else:
180        pattern = ("WTF::HashMap<%s, %s>" if _for_blink else
181                   "std::unordered_map<%s, %s>")
182    else:
183      pattern = "mojo::WTFMap<%s, %s>" if _for_blink else "mojo::Map<%s, %s>"
184    return pattern % (GetCppWrapperType(kind.key_kind),
185                      GetCppWrapperType(kind.value_kind))
186  if mojom.IsInterfaceKind(kind):
187    return "%sPtr" % GetNameForKind(kind)
188  if mojom.IsInterfaceRequestKind(kind):
189    return "%sRequest" % GetNameForKind(kind.kind)
190  if mojom.IsAssociatedInterfaceKind(kind):
191    return "%sAssociatedPtrInfo" % GetNameForKind(kind.kind)
192  if mojom.IsAssociatedInterfaceRequestKind(kind):
193    return "%sAssociatedRequest" % GetNameForKind(kind.kind)
194  if mojom.IsStringKind(kind):
195    if _for_blink:
196      return "WTF::String"
197    if not _use_new_wrapper_types:
198      return "mojo::String"
199    return ("base::Optional<std::string>" if mojom.IsNullableKind(kind) else
200            "std::string")
201  if mojom.IsGenericHandleKind(kind):
202    return "mojo::ScopedHandle"
203  if mojom.IsDataPipeConsumerKind(kind):
204    return "mojo::ScopedDataPipeConsumerHandle"
205  if mojom.IsDataPipeProducerKind(kind):
206    return "mojo::ScopedDataPipeProducerHandle"
207  if mojom.IsMessagePipeKind(kind):
208    return "mojo::ScopedMessagePipeHandle"
209  if mojom.IsSharedBufferKind(kind):
210    return "mojo::ScopedSharedBufferHandle"
211  if not kind in _kind_to_cpp_type:
212    raise Exception("Unrecognized kind %s" % kind.spec)
213  return _kind_to_cpp_type[kind]
214
215def IsMoveOnlyKind(kind):
216  if IsTypemappedKind(kind):
217    if mojom.IsEnumKind(kind):
218      return False
219    return _current_typemap[GetFullMojomNameForKind(kind)]["move_only"]
220  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
221    return True
222  if mojom.IsArrayKind(kind):
223    return IsMoveOnlyKind(kind.kind) if _use_new_wrapper_types else True
224  if mojom.IsMapKind(kind):
225    return IsMoveOnlyKind(kind.value_kind) if _use_new_wrapper_types else True
226  if mojom.IsAnyHandleOrInterfaceKind(kind):
227    return True
228  return False
229
230def ShouldPassParamByValue(kind):
231  return (not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind)
232
233def GetCppWrapperParamType(kind):
234  cpp_wrapper_type = GetCppWrapperType(kind)
235  return (cpp_wrapper_type if ShouldPassParamByValue(kind)
236                           else "const %s&" % cpp_wrapper_type)
237
238def GetCppFieldType(kind):
239  if mojom.IsStructKind(kind):
240    return ("mojo::internal::Pointer<%s>" %
241        GetNameForKind(kind, internal=True))
242  if mojom.IsUnionKind(kind):
243    return "%s" % GetNameForKind(kind, internal=True)
244  if mojom.IsArrayKind(kind):
245    return ("mojo::internal::Pointer<mojo::internal::Array_Data<%s>>" %
246            GetCppFieldType(kind.kind))
247  if mojom.IsMapKind(kind):
248    return ("mojo::internal::Pointer<mojo::internal::Map_Data<%s, %s>>" %
249            (GetCppFieldType(kind.key_kind), GetCppFieldType(kind.value_kind)))
250  if mojom.IsInterfaceKind(kind):
251    return "mojo::internal::Interface_Data"
252  if mojom.IsInterfaceRequestKind(kind):
253    return "mojo::internal::Handle_Data"
254  if mojom.IsAssociatedInterfaceKind(kind):
255    return "mojo::internal::AssociatedInterface_Data"
256  if mojom.IsAssociatedInterfaceRequestKind(kind):
257    return "mojo::internal::AssociatedInterfaceRequest_Data"
258  if mojom.IsEnumKind(kind):
259    return "int32_t"
260  if mojom.IsStringKind(kind):
261    return "mojo::internal::Pointer<mojo::internal::String_Data>"
262  if mojom.IsAnyHandleKind(kind):
263    return "mojo::internal::Handle_Data"
264  return _kind_to_cpp_type[kind]
265
266def GetCppUnionFieldType(kind):
267  if mojom.IsUnionKind(kind):
268    return ("mojo::internal::Pointer<%s>" % GetNameForKind(kind, internal=True))
269  return GetCppFieldType(kind)
270
271def GetUnionGetterReturnType(kind):
272  if mojom.IsReferenceKind(kind):
273    return "%s&" % GetCppWrapperType(kind)
274  return GetCppWrapperType(kind)
275
276def GetUnmappedTypeForSerializer(kind):
277  if mojom.IsEnumKind(kind):
278    return GetQualifiedNameForKind(kind)
279  if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
280    return "%sPtr" % GetQualifiedNameForKind(kind)
281  if mojom.IsArrayKind(kind):
282    return "mojo::Array<%s>" % GetUnmappedTypeForSerializer(kind.kind)
283  if mojom.IsMapKind(kind):
284    return "mojo::Map<%s, %s>" % (
285        GetUnmappedTypeForSerializer(kind.key_kind),
286        GetUnmappedTypeForSerializer(kind.value_kind))
287  if mojom.IsInterfaceKind(kind):
288    return "%sPtr" % GetQualifiedNameForKind(kind)
289  if mojom.IsInterfaceRequestKind(kind):
290    return "%sRequest" % GetQualifiedNameForKind(kind.kind)
291  if mojom.IsAssociatedInterfaceKind(kind):
292    return "%sAssociatedPtrInfo" % GetQualifiedNameForKind(kind.kind)
293  if mojom.IsAssociatedInterfaceRequestKind(kind):
294    return "%sAssociatedRequest" % GetQualifiedNameForKind(kind.kind)
295  if mojom.IsStringKind(kind):
296    return "mojo::String"
297  if mojom.IsGenericHandleKind(kind):
298    return "mojo::ScopedHandle"
299  if mojom.IsDataPipeConsumerKind(kind):
300    return "mojo::ScopedDataPipeConsumerHandle"
301  if mojom.IsDataPipeProducerKind(kind):
302    return "mojo::ScopedDataPipeProducerHandle"
303  if mojom.IsMessagePipeKind(kind):
304    return "mojo::ScopedMessagePipeHandle"
305  if mojom.IsSharedBufferKind(kind):
306    return "mojo::ScopedSharedBufferHandle"
307  return _kind_to_cpp_type[kind]
308
309def TranslateConstants(token, kind):
310  if isinstance(token, mojom.NamedValue):
311    return _NameFormatter(token, _variant).FormatForCpp()
312
313  if isinstance(token, mojom.BuiltinValue):
314    if token.value == "double.INFINITY" or token.value == "float.INFINITY":
315      return "INFINITY";
316    if token.value == "double.NEGATIVE_INFINITY" or \
317       token.value == "float.NEGATIVE_INFINITY":
318      return "-INFINITY";
319    if token.value == "double.NAN" or token.value == "float.NAN":
320      return "NAN";
321
322  if (kind is not None and mojom.IsFloatKind(kind)):
323      return token if token.isdigit() else token + "f";
324
325  # Per C++11, 2.14.2, the type of an integer literal is the first of the
326  # corresponding list in Table 6 in which its value can be represented. In this
327  # case, the list for decimal constants with no suffix is:
328  #   int, long int, long long int
329  # The standard considers a program ill-formed if it contains an integer
330  # literal that cannot be represented by any of the allowed types.
331  #
332  # As it turns out, MSVC doesn't bother trying to fall back to long long int,
333  # so the integral constant -2147483648 causes it grief: it decides to
334  # represent 2147483648 as an unsigned integer, and then warns that the unary
335  # minus operator doesn't make sense on unsigned types. Doh!
336  if kind == mojom.INT32 and token == "-2147483648":
337    return "(-%d - 1) /* %s */" % (
338        2**31 - 1, "Workaround for MSVC bug; see https://crbug.com/445618")
339
340  return "%s%s" % (token, _kind_to_cpp_literal_suffix.get(kind, ""))
341
342def ExpressionToText(value, kind=None):
343  return TranslateConstants(value, kind)
344
345def ShouldInlineStruct(struct):
346  # TODO(darin): Base this on the size of the wrapper class.
347  if len(struct.fields) > 4:
348    return False
349  for field in struct.fields:
350    if mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind):
351      return False
352  return True
353
354def ShouldInlineUnion(union):
355  return not any(
356      mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind)
357           for field in union.fields)
358
359def GetContainerValidateParamsCtorArgs(kind):
360  if mojom.IsStringKind(kind):
361    expected_num_elements = 0
362    element_is_nullable = False
363    key_validate_params = "nullptr"
364    element_validate_params = "nullptr"
365    enum_validate_func = "nullptr"
366  elif mojom.IsMapKind(kind):
367    expected_num_elements = 0
368    element_is_nullable = False
369    key_validate_params = GetNewContainerValidateParams(mojom.Array(
370        kind=kind.key_kind))
371    element_validate_params = GetNewContainerValidateParams(mojom.Array(
372        kind=kind.value_kind))
373    enum_validate_func = "nullptr"
374  else:  # mojom.IsArrayKind(kind)
375    expected_num_elements = generator.ExpectedArraySize(kind) or 0
376    element_is_nullable = mojom.IsNullableKind(kind.kind)
377    key_validate_params = "nullptr"
378    element_validate_params = GetNewContainerValidateParams(kind.kind)
379    if mojom.IsEnumKind(kind.kind):
380      enum_validate_func = ("%s::Validate" %
381                            GetQualifiedNameForKind(kind.kind, internal=True))
382    else:
383      enum_validate_func = "nullptr"
384
385  if enum_validate_func == "nullptr":
386    if key_validate_params == "nullptr":
387      return "%d, %s, %s" % (expected_num_elements,
388                             "true" if element_is_nullable else "false",
389                             element_validate_params)
390    else:
391      return "%s, %s" % (key_validate_params, element_validate_params)
392  else:
393    return "%d, %s" % (expected_num_elements, enum_validate_func)
394
395def GetNewContainerValidateParams(kind):
396  if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and
397      not mojom.IsStringKind(kind)):
398    return "nullptr"
399
400  return "new mojo::internal::ContainerValidateParams(%s)" % (
401      GetContainerValidateParamsCtorArgs(kind))
402
403class Generator(generator.Generator):
404
405  cpp_filters = {
406    "constant_value": ConstantValue,
407    "cpp_wrapper_param_type": GetCppWrapperParamType,
408    "cpp_field_type": GetCppFieldType,
409    "cpp_union_field_type": GetCppUnionFieldType,
410    "cpp_pod_type": GetCppPodType,
411    "cpp_union_getter_return_type": GetUnionGetterReturnType,
412    "cpp_wrapper_type": GetCppWrapperType,
413    "default_value": DefaultValue,
414    "expression_to_text": ExpressionToText,
415    "get_container_validate_params_ctor_args":
416    GetContainerValidateParamsCtorArgs,
417    "get_name_for_kind": GetNameForKind,
418    "get_pad": pack.GetPad,
419    "get_qualified_name_for_kind": GetQualifiedNameForKind,
420    "has_callbacks": mojom.HasCallbacks,
421    "has_sync_methods": mojom.HasSyncMethods,
422    "should_inline": ShouldInlineStruct,
423    "should_inline_union": ShouldInlineUnion,
424    "is_array_kind": mojom.IsArrayKind,
425    "is_enum_kind": mojom.IsEnumKind,
426    "is_integral_kind": mojom.IsIntegralKind,
427    "is_native_only_kind": IsNativeOnlyKind,
428    "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind,
429    "is_associated_kind": mojom.IsAssociatedKind,
430    "is_map_kind": mojom.IsMapKind,
431    "is_nullable_kind": mojom.IsNullableKind,
432    "is_object_kind": mojom.IsObjectKind,
433    "is_string_kind": mojom.IsStringKind,
434    "is_struct_kind": mojom.IsStructKind,
435    "is_typemapped_kind": IsTypemappedKind,
436    "is_union_kind": mojom.IsUnionKind,
437    "passes_associated_kinds": mojom.PassesAssociatedKinds,
438    "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
439    "stylize_method": generator.StudlyCapsToCamel,
440    "under_to_camel": generator.UnderToCamel,
441    "unmapped_type_for_serializer": GetUnmappedTypeForSerializer,
442  }
443
444  def GetExtraTraitsHeaders(self):
445    extra_headers = set()
446    for entry in self.typemap.itervalues():
447      extra_headers.update(entry.get("traits_headers", []))
448    return list(extra_headers)
449
450  def GetExtraPublicHeaders(self):
451    extra_headers = set()
452    for entry in self.typemap.itervalues():
453      extra_headers.update(entry.get("public_headers", []))
454    return list(extra_headers)
455
456  def GetJinjaExports(self):
457    return {
458      "module": self.module,
459      "namespace": self.module.namespace,
460      "namespaces_as_array": NamespaceToArray(self.module.namespace),
461      "imports": self.module.imports,
462      "kinds": self.module.kinds,
463      "enums": self.module.enums,
464      "structs": self.GetStructs(),
465      "unions": self.GetUnions(),
466      "interfaces": self.GetInterfaces(),
467      "variant": self.variant,
468      "extra_traits_headers": self.GetExtraTraitsHeaders(),
469      "extra_public_headers": self.GetExtraPublicHeaders(),
470      "for_blink": self.for_blink,
471      "use_new_wrapper_types": self.use_new_wrapper_types,
472    }
473
474  @staticmethod
475  def GetTemplatePrefix():
476    return "cpp_templates"
477
478  @classmethod
479  def GetFilters(cls):
480    return cls.cpp_filters
481
482  @UseJinja("module.h.tmpl")
483  def GenerateModuleHeader(self):
484    return self.GetJinjaExports()
485
486  @UseJinja("module-internal.h.tmpl")
487  def GenerateModuleInternalHeader(self):
488    return self.GetJinjaExports()
489
490  @UseJinja("module.cc.tmpl")
491  def GenerateModuleSource(self):
492    return self.GetJinjaExports()
493
494  def GenerateFiles(self, args):
495    global _current_typemap
496    _current_typemap = self.typemap
497    global _for_blink
498    _for_blink = self.for_blink
499    global _use_new_wrapper_types
500    _use_new_wrapper_types = self.use_new_wrapper_types
501    global _variant
502    _variant = self.variant
503    suffix = "-%s" % self.variant if self.variant else ""
504    self.Write(self.GenerateModuleHeader(),
505        self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
506    self.Write(self.GenerateModuleInternalHeader(),
507        self.MatchMojomFilePath("%s%s-internal.h" % (self.module.name, suffix)))
508    self.Write(self.GenerateModuleSource(),
509        self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
510