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.HANDLE:                "mojo::Handle",
23  mojom.DCPIPE:                "mojo::DataPipeConsumerHandle",
24  mojom.DPPIPE:                "mojo::DataPipeProducerHandle",
25  mojom.MSGPIPE:               "mojo::MessagePipeHandle",
26  mojom.SHAREDBUFFER:          "mojo::SharedBufferHandle",
27  mojom.NULLABLE_HANDLE:       "mojo::Handle",
28  mojom.NULLABLE_DCPIPE:       "mojo::DataPipeConsumerHandle",
29  mojom.NULLABLE_DPPIPE:       "mojo::DataPipeProducerHandle",
30  mojom.NULLABLE_MSGPIPE:      "mojo::MessagePipeHandle",
31  mojom.NULLABLE_SHAREDBUFFER: "mojo::SharedBufferHandle",
32  mojom.INT64:                 "int64_t",
33  mojom.UINT64:                "uint64_t",
34  mojom.DOUBLE:                "double",
35}
36
37_kind_to_cpp_literal_suffix = {
38  mojom.UINT8:        "U",
39  mojom.UINT16:       "U",
40  mojom.UINT32:       "U",
41  mojom.FLOAT:        "f",
42  mojom.UINT64:       "ULL",
43}
44
45def ConstantValue(constant):
46  return ExpressionToText(constant.value, kind=constant.kind)
47
48def DefaultValue(field):
49  if field.default:
50    if mojom.IsStructKind(field.kind):
51      assert field.default == "default"
52      return "%s::New()" % GetNameForKind(field.kind)
53    return ExpressionToText(field.default, kind=field.kind)
54  return ""
55
56def NamespaceToArray(namespace):
57  return namespace.split('.') if namespace else []
58
59def GetNameForKind(kind, internal = False):
60  parts = []
61  if kind.imported_from:
62    parts.extend(NamespaceToArray(kind.imported_from["namespace"]))
63  if internal:
64    parts.append("internal")
65  if kind.parent_kind:
66    parts.append(kind.parent_kind.name)
67  parts.append(kind.name)
68  return "::".join(parts)
69
70def GetCppType(kind):
71  if mojom.IsStructKind(kind):
72    return "%s_Data*" % GetNameForKind(kind, internal=True)
73  if mojom.IsAnyArrayKind(kind):
74    return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind)
75  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
76    return "mojo::MessagePipeHandle"
77  if mojom.IsEnumKind(kind):
78    return "int32_t"
79  if mojom.IsStringKind(kind):
80    return "mojo::internal::String_Data*"
81  return _kind_to_cpp_type[kind]
82
83def GetCppPodType(kind):
84  if mojom.IsStringKind(kind):
85    return "char*"
86  return _kind_to_cpp_type[kind]
87
88def GetCppArrayArgWrapperType(kind):
89  if mojom.IsEnumKind(kind):
90    return GetNameForKind(kind)
91  if mojom.IsStructKind(kind):
92    return "%sPtr" % GetNameForKind(kind)
93  if mojom.IsAnyArrayKind(kind):
94    return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind)
95  if mojom.IsInterfaceKind(kind):
96    raise Exception("Arrays of interfaces not yet supported!")
97  if mojom.IsInterfaceRequestKind(kind):
98    raise Exception("Arrays of interface requests not yet supported!")
99  if mojom.IsStringKind(kind):
100    return "mojo::String"
101  if mojom.IsHandleKind(kind):
102    return "mojo::ScopedHandle"
103  if mojom.IsDataPipeConsumerKind(kind):
104    return "mojo::ScopedDataPipeConsumerHandle"
105  if mojom.IsDataPipeProducerKind(kind):
106    return "mojo::ScopedDataPipeProducerHandle"
107  if mojom.IsMessagePipeKind(kind):
108    return "mojo::ScopedMessagePipeHandle"
109  if mojom.IsSharedBufferKind(kind):
110    return "mojo::ScopedSharedBufferHandle"
111  return _kind_to_cpp_type[kind]
112
113def GetCppResultWrapperType(kind):
114  if mojom.IsEnumKind(kind):
115    return GetNameForKind(kind)
116  if mojom.IsStructKind(kind):
117    return "%sPtr" % GetNameForKind(kind)
118  if mojom.IsAnyArrayKind(kind):
119    return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
120  if mojom.IsInterfaceKind(kind):
121    return "%sPtr" % GetNameForKind(kind)
122  if mojom.IsInterfaceRequestKind(kind):
123    return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
124  if mojom.IsStringKind(kind):
125    return "mojo::String"
126  if mojom.IsHandleKind(kind):
127    return "mojo::ScopedHandle"
128  if mojom.IsDataPipeConsumerKind(kind):
129    return "mojo::ScopedDataPipeConsumerHandle"
130  if mojom.IsDataPipeProducerKind(kind):
131    return "mojo::ScopedDataPipeProducerHandle"
132  if mojom.IsMessagePipeKind(kind):
133    return "mojo::ScopedMessagePipeHandle"
134  if mojom.IsSharedBufferKind(kind):
135    return "mojo::ScopedSharedBufferHandle"
136  return _kind_to_cpp_type[kind]
137
138def GetCppWrapperType(kind):
139  if mojom.IsEnumKind(kind):
140    return GetNameForKind(kind)
141  if mojom.IsStructKind(kind):
142    return "%sPtr" % GetNameForKind(kind)
143  if mojom.IsAnyArrayKind(kind):
144    return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
145  if mojom.IsInterfaceKind(kind):
146    return "%sPtr" % GetNameForKind(kind)
147  if mojom.IsInterfaceRequestKind(kind):
148    raise Exception("InterfaceRequest fields not supported!")
149  if mojom.IsStringKind(kind):
150    return "mojo::String"
151  if mojom.IsHandleKind(kind):
152    return "mojo::ScopedHandle"
153  if mojom.IsDataPipeConsumerKind(kind):
154    return "mojo::ScopedDataPipeConsumerHandle"
155  if mojom.IsDataPipeProducerKind(kind):
156    return "mojo::ScopedDataPipeProducerHandle"
157  if mojom.IsMessagePipeKind(kind):
158    return "mojo::ScopedMessagePipeHandle"
159  if mojom.IsSharedBufferKind(kind):
160    return "mojo::ScopedSharedBufferHandle"
161  return _kind_to_cpp_type[kind]
162
163def GetCppConstWrapperType(kind):
164  if mojom.IsStructKind(kind):
165    return "%sPtr" % GetNameForKind(kind)
166  if mojom.IsAnyArrayKind(kind):
167    return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
168  if mojom.IsInterfaceKind(kind):
169    return "%sPtr" % GetNameForKind(kind)
170  if mojom.IsInterfaceRequestKind(kind):
171    return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
172  if mojom.IsEnumKind(kind):
173    return GetNameForKind(kind)
174  if mojom.IsStringKind(kind):
175    return "const mojo::String&"
176  if mojom.IsHandleKind(kind):
177    return "mojo::ScopedHandle"
178  if mojom.IsDataPipeConsumerKind(kind):
179    return "mojo::ScopedDataPipeConsumerHandle"
180  if mojom.IsDataPipeProducerKind(kind):
181    return "mojo::ScopedDataPipeProducerHandle"
182  if mojom.IsMessagePipeKind(kind):
183    return "mojo::ScopedMessagePipeHandle"
184  if mojom.IsSharedBufferKind(kind):
185    return "mojo::ScopedSharedBufferHandle"
186  if not kind in _kind_to_cpp_type:
187    print "missing:", kind.spec
188  return _kind_to_cpp_type[kind]
189
190def GetCppFieldType(kind):
191  if mojom.IsStructKind(kind):
192    return ("mojo::internal::StructPointer<%s_Data>" %
193        GetNameForKind(kind, internal=True))
194  if mojom.IsAnyArrayKind(kind):
195    return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind)
196  if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
197    return "mojo::MessagePipeHandle"
198  if mojom.IsEnumKind(kind):
199    return GetNameForKind(kind)
200  if mojom.IsStringKind(kind):
201    return "mojo::internal::StringPointer"
202  return _kind_to_cpp_type[kind]
203
204def IsStructWithHandles(struct):
205  for pf in struct.packed.packed_fields:
206    if mojom.IsAnyHandleKind(pf.field.kind):
207      return True
208  return False
209
210def TranslateConstants(token, kind):
211  if isinstance(token, mojom.NamedValue):
212    # Both variable and enum constants are constructed like:
213    # Namespace::Struct::CONSTANT_NAME
214    # For enums, CONSTANT_NAME is ENUM_NAME_ENUM_VALUE.
215    name = []
216    if token.imported_from:
217      name.extend(NamespaceToArray(token.namespace))
218    if token.parent_kind:
219      name.append(token.parent_kind.name)
220    if isinstance(token, mojom.EnumValue):
221      name.append(
222          "%s_%s" % (generator.CamelCaseToAllCaps(token.enum.name), token.name))
223    else:
224      name.append(token.name)
225    return "::".join(name)
226
227  if isinstance(token, mojom.BuiltinValue):
228    if token.value == "double.INFINITY" or token.value == "float.INFINITY":
229      return "INFINITY";
230    if token.value == "double.NEGATIVE_INFINITY" or \
231       token.value == "float.NEGATIVE_INFINITY":
232      return "-INFINITY";
233    if token.value == "double.NAN" or token.value == "float.NAN":
234      return "NAN";
235
236  if (kind is not None and mojom.IsFloatKind(kind)):
237      return token if token.isdigit() else token + "f";
238
239  return '%s%s' % (token, _kind_to_cpp_literal_suffix.get(kind, ''))
240
241def ExpressionToText(value, kind=None):
242  return TranslateConstants(value, kind)
243
244def ShouldInlineStruct(struct):
245  # TODO(darin): Base this on the size of the wrapper class.
246  if len(struct.fields) > 4:
247    return False
248  for field in struct.fields:
249    if mojom.IsMoveOnlyKind(field.kind):
250      return False
251  return True
252
253def GetArrayValidateParams(kind):
254  if not mojom.IsAnyArrayKind(kind) and not mojom.IsStringKind(kind):
255    return "mojo::internal::NoValidateParams"
256
257  if mojom.IsStringKind(kind):
258    expected_num_elements = 0
259    element_is_nullable = False
260    element_validate_params = "mojo::internal::NoValidateParams"
261  else:
262    expected_num_elements = generator.ExpectedArraySize(kind)
263    element_is_nullable = mojom.IsNullableKind(kind.kind)
264    element_validate_params = GetArrayValidateParams(kind.kind)
265
266  return "mojo::internal::ArrayValidateParams<%d, %s,\n%s> " % (
267      expected_num_elements,
268      'true' if element_is_nullable else 'false',
269      element_validate_params)
270
271_HEADER_SIZE = 8
272
273class Generator(generator.Generator):
274
275  cpp_filters = {
276    "constant_value": ConstantValue,
277    "cpp_const_wrapper_type": GetCppConstWrapperType,
278    "cpp_field_type": GetCppFieldType,
279    "cpp_pod_type": GetCppPodType,
280    "cpp_result_type": GetCppResultWrapperType,
281    "cpp_type": GetCppType,
282    "cpp_wrapper_type": GetCppWrapperType,
283    "default_value": DefaultValue,
284    "expected_array_size": generator.ExpectedArraySize,
285    "expression_to_text": ExpressionToText,
286    "get_array_validate_params": GetArrayValidateParams,
287    "get_name_for_kind": GetNameForKind,
288    "get_pad": pack.GetPad,
289    "has_callbacks": mojom.HasCallbacks,
290    "should_inline": ShouldInlineStruct,
291    "is_any_array_kind": mojom.IsAnyArrayKind,
292    "is_enum_kind": mojom.IsEnumKind,
293    "is_move_only_kind": mojom.IsMoveOnlyKind,
294    "is_any_handle_kind": mojom.IsAnyHandleKind,
295    "is_interface_kind": mojom.IsInterfaceKind,
296    "is_interface_request_kind": mojom.IsInterfaceRequestKind,
297    "is_nullable_kind": mojom.IsNullableKind,
298    "is_object_kind": mojom.IsObjectKind,
299    "is_string_kind": mojom.IsStringKind,
300    "is_struct_with_handles": IsStructWithHandles,
301    "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
302    "struct_from_method": generator.GetStructFromMethod,
303    "response_struct_from_method": generator.GetResponseStructFromMethod,
304    "stylize_method": generator.StudlyCapsToCamel,
305    "to_all_caps": generator.CamelCaseToAllCaps,
306  }
307
308  def GetJinjaExports(self):
309    return {
310      "module": self.module,
311      "namespace": self.module.namespace,
312      "namespaces_as_array": NamespaceToArray(self.module.namespace),
313      "imports": self.module.imports,
314      "kinds": self.module.kinds,
315      "enums": self.module.enums,
316      "structs": self.GetStructs(),
317      "interfaces": self.module.interfaces,
318    }
319
320  @UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
321  def GenerateModuleHeader(self):
322    return self.GetJinjaExports()
323
324  @UseJinja("cpp_templates/module-internal.h.tmpl", filters=cpp_filters)
325  def GenerateModuleInternalHeader(self):
326    return self.GetJinjaExports()
327
328  @UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters)
329  def GenerateModuleSource(self):
330    return self.GetJinjaExports()
331
332  def GenerateFiles(self, args):
333    self.Write(self.GenerateModuleHeader(), "%s.h" % self.module.name)
334    self.Write(self.GenerateModuleInternalHeader(),
335        "%s-internal.h" % self.module.name)
336    self.Write(self.GenerateModuleSource(), "%s.cc" % self.module.name)
337