1 # Copyright (c) 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.
5 """Generates C++ source files from a mojom.Module."""
13 from string import Template
15 # mojom_cpp_generator provides a way to generate c++ code from a mojom.Module.
16 # cpp = mojom_cpp_generator.CPPGenerator(module)
17 # cpp.GenerateFiles("/tmp/g")
19 class DependentKinds(set):
20 """Set subclass to find the unique set of non POD types."""
21 def AddKind(self, kind):
22 if isinstance(kind, mojom.Struct):
24 if isinstance(kind, mojom.Array):
25 self.AddKind(kind.kind)
28 class Forwards(object):
29 """Helper class to maintain unique set of forward declarations."""
31 self.kinds = DependentKinds()
34 self.kinds.AddKind(kind)
39 lambda kind: "class %s;" % kind.name.capitalize(), self.kinds)))
43 """Helper class to maintain list of template expanded lines."""
44 def __init__(self, template):
45 self.template = template
48 def Add(self, map = {}, **substitutions):
49 if len(substitutions) > 0:
51 map.update(substitutions)
53 self.lines.append(self.template.substitute(map))
56 return '\n'.join(self.lines)
59 def GetStructFromMethod(interface, method):
60 """Converts a method's parameters into the fields of a struct."""
62 "%s_%s_Params" % (interface.name.capitalize(), method.name.capitalize())
63 struct = mojom.Struct(params_class)
64 for param in method.parameters:
65 struct.AddField(param.name, param.kind, param.ordinal)
68 def IsPointerKind(kind):
69 return isinstance(kind, (mojom.Struct, mojom.Array)) or kind.spec == 's'
71 class CPPGenerator(object):
73 struct_serialization_compute_template = \
74 Template(" +\n mojo::internal::ComputeSizeOf($NAME->$FIELD())")
75 struct_serialization_clone_template = Template(
76 " clone->set_$FIELD(mojo::internal::Clone($NAME->$FIELD(), buf));")
77 struct_serialization_encode_template = Template(
78 " Encode(&$NAME->${FIELD}_, handles);")
79 struct_serialization_encode_handle_template = Template(
80 " EncodeHandle(&$NAME->${FIELD}_, handles);")
81 struct_serialization_decode_template = Template(
82 " if (!Decode(&$NAME->${FIELD}_, message))\n"
84 struct_serialization_decode_handle_template = Template(
85 " if (!DecodeHandle(&$NAME->${FIELD}_, message.handles))\n"
87 param_set_template = Template(" params->set_$NAME($NAME);")
88 param_struct_set_template = Template(
89 " params->set_$NAME(mojo::internal::Clone($NAME, builder.buffer()));")
90 param_struct_compute_template = Template(
91 " payload_size += mojo::internal::ComputeSizeOf($NAME);")
92 field_template = Template(" $TYPE ${FIELD}_;")
93 bool_field_template = Template(" uint8_t ${FIELD}_ : 1;")
95 Template(" void set_$FIELD($TYPE $FIELD) { ${FIELD}_ = $FIELD; }")
96 ptr_setter_template = \
97 Template(" void set_$FIELD($TYPE $FIELD) { ${FIELD}_.ptr = $FIELD; }")
99 Template(" $TYPE $FIELD() const { return ${FIELD}_; }")
100 ptr_getter_template = \
101 Template(" $TYPE $FIELD() const { return ${FIELD}_.ptr; }")
102 pad_template = Template(" uint8_t _pad${COUNT}_[$PAD];")
104 Template("const uint32_t k${INTERFACE}_${METHOD}_Name = $NAME;")
110 mojom.INT8: "int8_t",
111 mojom.UINT8: "uint8_t",
112 mojom.INT16: "int16_t",
113 mojom.UINT16: "uint16_t",
114 mojom.INT32: "int32_t",
115 mojom.UINT32: "uint32_t",
116 mojom.FLOAT: "float",
117 mojom.HANDLE: "mojo::Handle",
118 mojom.INT64: "int64_t",
119 mojom.UINT64: "uint64_t",
120 mojom.DOUBLE: "double",
124 def GetTemplate(cls, template_name):
125 if template_name not in cls.templates:
126 dir = os.path.dirname(__file__)
127 filename = os.path.join(dir, 'cpp_templates', template_name)
128 filename = filename.replace('.h', '.h-template')
129 filename = filename.replace('.cc', '.cc-template')
130 with open(filename, 'r') as file:
131 template = Template(file.read())
132 cls.templates[template_name] = template
133 return cls.templates[template_name]
136 def GetType(cls, kind):
137 if isinstance(kind, mojom.Struct):
138 return "%s*" % kind.name.capitalize()
139 if isinstance(kind, mojom.Array):
140 return "mojo::Array<%s>*" % cls.GetType(kind.kind)
142 return "mojo::String*"
143 return cls.kind_to_type[kind]
146 def GetConstType(cls, kind):
147 if isinstance(kind, mojom.Struct):
148 return "const %s*" % kind.name.capitalize()
149 if isinstance(kind, mojom.Array):
150 return "const mojo::Array<%s>*" % cls.GetConstType(kind.kind)
152 return "const mojo::String*"
153 return cls.kind_to_type[kind]
156 def GetGetterLine(cls, field):
157 subs = {'FIELD': field.name, 'TYPE': cls.GetType(field.kind)}
158 if IsPointerKind(field.kind):
159 return cls.ptr_getter_template.substitute(subs)
161 return cls.getter_template.substitute(subs)
164 def GetSetterLine(cls, field):
165 subs = {'FIELD': field.name, 'TYPE': cls.GetType(field.kind)}
166 if IsPointerKind(field.kind):
167 return cls.ptr_setter_template.substitute(subs)
169 return cls.setter_template.substitute(subs)
172 def GetFieldLine(cls, field):
175 return cls.bool_field_template.substitute(FIELD=field.name)
177 if isinstance(kind, mojom.Struct):
178 itype = "mojo::internal::StructPointer<%s>" % kind.name.capitalize()
179 elif isinstance(kind, mojom.Array):
180 itype = "mojo::internal::ArrayPointer<%s>" % cls.GetType(kind.kind)
181 elif kind.spec == 's':
182 itype = "mojo::internal::StringPointer"
184 itype = cls.kind_to_type[kind]
185 return cls.field_template.substitute(FIELD=field.name, TYPE=itype)
188 def GetCaseLine(cls, interface, method):
189 params = map(lambda param: "params->%s()" % param.name, method.parameters)
190 method_call = "%s(%s);" % (method.name, ", ".join(params))
192 return cls.GetTemplate("interface_stub_case").substitute(
193 CLASS = interface.name,
194 METHOD = method.name,
195 METHOD_CALL = method_call);
198 def GetSerializedFields(cls, ps):
200 for pf in ps.packed_fields:
201 if IsPointerKind(pf.field.kind):
202 fields.append(pf.field)
206 def GetHandleFields(cls, ps):
208 for pf in ps.packed_fields:
209 if pf.field.kind.spec == 'h':
210 fields.append(pf.field)
213 def GetHeaderGuard(self, name):
214 return "MOJO_GENERATED_BINDINGS_%s_%s_H_" % \
215 (self.module.name.upper(), name.upper())
217 def GetHeaderFile(self, *components):
218 component_string = '_'.join(components)
221 "%s_%s.h" % (self.module.name.lower(), component_string.lower()))
223 # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all files
225 def __init__(self, module, header_dir, output_dir=None):
227 self.header_dir = header_dir
228 self.output_dir = output_dir
230 def WriteTemplateToFile(self, template_name, name, **substitutions):
231 template = self.GetTemplate(template_name)
232 filename = "%s_%s" % (self.module.name.lower(), template_name)
233 filename = filename.replace("interface", name.lower())
234 filename = filename.replace("struct", name.lower())
235 substitutions['YEAR'] = datetime.date.today().year
236 substitutions['NAMESPACE'] = self.module.namespace
237 if self.output_dir is None:
240 file = open(os.path.join(self.output_dir, filename), "w+")
242 file.write(template.substitute(substitutions))
244 if self.output_dir is not None:
247 def GetStructDeclaration(self, name, ps):
248 params_template = self.GetTemplate("struct_declaration")
253 num_fields = len(ps.packed_fields)
254 for i in xrange(num_fields):
255 pf = ps.packed_fields[i]
257 fields.append(self.GetFieldLine(field))
258 if i < (num_fields - 1):
259 next_pf = ps.packed_fields[i+1]
260 pad = next_pf.offset - (pf.offset + pf.size)
262 fields.append(self.pad_template.substitute(COUNT=pad_count, PAD=pad))
264 setters.append(self.GetSetterLine(field))
265 getters.append(self.GetGetterLine(field))
268 last_field = ps.packed_fields[num_fields - 1]
269 offset = last_field.offset + last_field.size
270 pad = mojom_pack.GetPad(offset, 8)
272 fields.append(self.pad_template.substitute(COUNT=pad_count, PAD=pad))
277 return params_template.substitute(
279 SETTERS = '\n'.join(setters),
280 GETTERS = '\n'.join(getters),
281 FIELDS = '\n'.join(fields),
282 SIZE = size + self.HEADER_SIZE)
284 def GetStructImplementation(self, name, ps):
285 return self.GetTemplate("struct_implementation").substitute(
287 NUM_FIELDS = len(ps.packed_fields))
289 def GetStructSerialization(self, class_name, param_name, ps):
291 encodes = Lines(self.struct_serialization_encode_template)
292 encode_handles = Lines(self.struct_serialization_encode_handle_template)
293 decodes = Lines(self.struct_serialization_decode_template)
294 decode_handles = Lines(self.struct_serialization_decode_handle_template)
295 fields = self.GetSerializedFields(ps)
296 handle_fields = self.GetHandleFields(ps)
298 substitutions = {'NAME': param_name, 'FIELD': field.name.lower()}
299 encodes.Add(substitutions)
300 decodes.Add(substitutions)
301 for field in handle_fields:
302 substitutions = {'NAME': param_name, 'FIELD': field.name.lower()}
303 encode_handles.Add(substitutions)
304 decode_handles.Add(substitutions)
305 return self.GetTemplate("struct_serialization").substitute(
306 CLASS = "%s::%s" % (self.module.namespace.lower(), class_name),
310 ENCODE_HANDLES = encode_handles,
311 DECODE_HANDLES = decode_handles)
313 def GenerateStructHeader(self, ps):
315 forwards = Forwards()
316 for field in struct.fields:
317 forwards.Add(field.kind)
319 self.WriteTemplateToFile("struct.h", struct.name,
320 HEADER_GUARD = self.GetHeaderGuard(struct.name),
321 CLASS = struct.name.capitalize(),
323 DECLARATION = self.GetStructDeclaration(struct.name.capitalize(), ps))
325 def GenerateStructSource(self, ps):
327 header = self.GetHeaderFile(struct.name)
328 implementation = self.GetStructImplementation(struct.name.capitalize(), ps)
329 self.WriteTemplateToFile("struct.cc", struct.name,
330 CLASS = struct.name.capitalize(),
331 NUM_FIELDS = len(struct.fields),
333 IMPLEMENTATION = implementation)
335 def GenerateStructSerializationHeader(self, ps):
337 self.WriteTemplateToFile("struct_serialization.h", struct.name,
338 HEADER_GUARD = self.GetHeaderGuard(struct.name + "_SERIALIZATION"),
339 CLASS = struct.name.capitalize(),
340 FULL_CLASS = "%s::%s" % \
341 (self.module.namespace, struct.name.capitalize()))
343 def GenerateStructSerializationSource(self, ps):
345 serialization_header = self.GetHeaderFile(struct.name, "serialization")
347 kinds = DependentKinds()
348 for field in struct.fields:
349 kinds.AddKind(field.kind)
351 map(lambda kind: self.GetHeaderFile(kind.name, "serialization"), kinds)
352 headers.append(self.GetHeaderFile(struct.name))
353 includes = map(lambda header: "#include \"%s\"" % header, sorted(headers))
355 class_header = self.GetHeaderFile(struct.name)
356 clones = Lines(self.struct_serialization_clone_template)
357 sizes = " return sizeof(*%s)" % struct.name.lower()
358 fields = self.GetSerializedFields(ps)
360 substitutions = {'NAME': struct.name.lower(), 'FIELD': field.name.lower()}
362 self.struct_serialization_compute_template.substitute(substitutions)
363 clones.Add(substitutions)
366 self.GetStructSerialization(struct.name.capitalize(), struct.name, ps)
367 self.WriteTemplateToFile("struct_serialization.cc", struct.name,
368 NAME = struct.name.lower(),
370 (self.module.namespace.lower(), struct.name.capitalize()),
371 SERIALIZATION_HEADER = serialization_header,
372 INCLUDES = '\n'.join(includes),
375 SERIALIZATION = serialization)
377 def GenerateInterfaceHeader(self, interface):
379 forwards = Forwards()
380 for method in interface.methods:
382 for param in method.parameters:
383 forwards.Add(param.kind)
384 params.append("%s %s" % (self.GetConstType(param.kind), param.name))
386 " virtual void %s(%s) = 0;" % (method.name, ", ".join(params)))
388 self.WriteTemplateToFile("interface.h", interface.name,
389 HEADER_GUARD = self.GetHeaderGuard(interface.name),
390 CLASS = interface.name.capitalize(),
392 METHODS = '\n'.join(methods))
394 def GenerateInterfaceStubHeader(self, interface):
395 header = self.GetHeaderFile(interface.name)
396 self.WriteTemplateToFile("interface_stub.h", interface.name,
397 HEADER_GUARD = self.GetHeaderGuard(interface.name + "_STUB"),
398 CLASS = interface.name.capitalize(),
401 def GenerateInterfaceStubSource(self, interface):
402 stub_header = self.GetHeaderFile(interface.name, "stub")
403 serialization_header = self.GetHeaderFile(interface.name, "serialization")
405 for method in interface.methods:
406 cases.append(self.GetCaseLine(interface, method))
407 self.WriteTemplateToFile("interface_stub.cc", interface.name,
408 CLASS = interface.name.capitalize(),
409 CASES = '\n'.join(cases),
410 STUB_HEADER = stub_header,
411 SERIALIZATION_HEADER = serialization_header)
413 def GenerateInterfaceSerializationHeader(self, interface):
414 kinds = DependentKinds()
415 for method in interface.methods:
416 for param in method.parameters:
417 kinds.AddKind(param.kind)
419 map(lambda kind: self.GetHeaderFile(kind.name, "serialization"), kinds)
420 headers.append(self.GetHeaderFile(interface.name))
421 headers.append("mojo/public/bindings/lib/bindings_serialization.h")
422 includes = map(lambda header: "#include \"%s\"" % header, sorted(headers))
428 template_declaration = self.GetTemplate("template_declaration")
429 for method in interface.methods:
430 names.append(self.name_template.substitute(
431 INTERFACE = interface.name.capitalize(),
432 METHOD = method.name.capitalize(),
436 struct = GetStructFromMethod(interface, method)
437 ps = mojom_pack.PackedStruct(struct)
438 param_classes.append(self.GetStructDeclaration(struct.name, ps))
439 param_templates.append(template_declaration.substitute(CLASS=struct.name))
441 self.WriteTemplateToFile("interface_serialization.h", interface.name,
442 HEADER_GUARD = self.GetHeaderGuard(interface.name + "_SERIALIZATION"),
443 INCLUDES = '\n'.join(includes),
444 NAMES = '\n'.join(names),
445 PARAM_CLASSES = '\n'.join(param_classes),
446 PARAM_TEMPLATES = '\n'.join(param_templates))
448 def GenerateInterfaceSerializationSource(self, interface):
451 for method in interface.methods:
452 struct = GetStructFromMethod(interface, method)
453 ps = mojom_pack.PackedStruct(struct)
454 implementations.append(self.GetStructImplementation(struct.name, ps))
455 serializations.append(
456 self.GetStructSerialization("internal::" + struct.name, "params", ps))
457 self.WriteTemplateToFile("interface_serialization.cc", interface.name,
458 HEADER = self.GetHeaderFile(interface.name.lower(), "serialization"),
459 IMPLEMENTATIONS = '\n'.join(implementations),
460 SERIALIZATIONS = '\n'.join(serializations))
462 def GenerateInterfaceProxyHeader(self, interface):
464 for method in interface.methods:
466 lambda param: "%s %s" % (self.GetConstType(param.kind), param.name),
469 " virtual void %s(%s) MOJO_OVERRIDE;" \
470 % (method.name, ", ".join(params)))
472 self.WriteTemplateToFile("interface_proxy.h", interface.name,
473 HEADER_GUARD = self.GetHeaderGuard(interface.name + "_PROXY"),
474 HEADER = self.GetHeaderFile(interface.name),
475 CLASS = interface.name.capitalize(),
476 METHODS = '\n'.join(methods))
478 def GenerateInterfaceProxySource(self, interface):
479 implementations = Lines(self.GetTemplate("proxy_implementation"))
480 for method in interface.methods:
482 computes = Lines(self.param_struct_compute_template)
483 for param in method.parameters:
484 if IsPointerKind(param.kind):
486 self.param_struct_set_template.substitute(NAME=param.name))
487 computes.Add(NAME=param.name)
489 sets.append(self.param_set_template.substitute(NAME=param.name))
491 lambda param: "%s %s" % (self.GetConstType(param.kind), param.name),
494 "internal::k%s_%s_Name" % (interface.name.capitalize(), method.name)
496 "internal::%s_%s_Params" % (interface.name.capitalize(), method.name)
499 CLASS = interface.name.capitalize(),
500 METHOD = method.name,
502 PARAMS = params_name,
503 PARAMS_LIST = ', '.join(params_list),
505 SETS = '\n'.join(sets))
506 self.WriteTemplateToFile("interface_proxy.cc", interface.name,
507 HEADER = self.GetHeaderFile(interface.name, "proxy"),
508 SERIALIZATION_HEADER = \
509 self.GetHeaderFile(interface.name, "serialization"),
510 CLASS = interface.name.capitalize(),
511 IMPLEMENTATIONS = implementations)
513 def GenerateFiles(self):
514 for struct in self.module.structs:
515 ps = mojom_pack.PackedStruct(struct)
516 self.GenerateStructHeader(ps)
517 self.GenerateStructSource(ps)
518 self.GenerateStructSerializationHeader(ps)
519 self.GenerateStructSerializationSource(ps)
520 for interface in self.module.interfaces:
521 self.GenerateInterfaceHeader(interface)
522 self.GenerateInterfaceStubHeader(interface)
523 self.GenerateInterfaceStubSource(interface)
524 self.GenerateInterfaceSerializationHeader(interface)
525 self.GenerateInterfaceSerializationSource(interface)
526 self.GenerateInterfaceProxyHeader(interface)
527 self.GenerateInterfaceProxySource(interface)