- add sources.
[platform/framework/web/crosswalk.git] / src / mojo / public / bindings / generators / mojom_cpp_generator.py
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.
4
5 """Generates C++ source files from a mojom.Module."""
6
7 import datetime
8 import mojom
9 import mojom_pack
10 import os
11 import sys
12
13 from string import Template
14
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")
18
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):
23       self.add(kind)
24     if isinstance(kind, mojom.Array):
25       self.AddKind(kind.kind)
26
27
28 class Forwards(object):
29   """Helper class to maintain unique set of forward declarations."""
30   def __init__(self):
31     self.kinds = DependentKinds()
32
33   def Add(self, kind):
34     self.kinds.AddKind(kind)
35
36   def __repr__(self):
37     return '\n'.join(
38       sorted(map(
39           lambda kind: "class %s;" % kind.name.capitalize(), self.kinds)))
40
41
42 class Lines(object):
43   """Helper class to maintain list of template expanded lines."""
44   def __init__(self, template):
45     self.template = template
46     self.lines = []
47
48   def Add(self, map = {}, **substitutions):
49     if len(substitutions) > 0:
50       map = map.copy()
51       map.update(substitutions)
52
53     self.lines.append(self.template.substitute(map))
54
55   def __repr__(self):
56     return '\n'.join(self.lines)
57
58
59 def GetStructFromMethod(interface, method):
60   """Converts a method's parameters into the fields of a struct."""
61   params_class = \
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)
66   return struct
67
68 def IsPointerKind(kind):
69   return isinstance(kind, (mojom.Struct, mojom.Array)) or kind.spec == 's'
70
71 class CPPGenerator(object):
72
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"
83       "    return false;")
84   struct_serialization_decode_handle_template = Template(
85       "  if (!DecodeHandle(&$NAME->${FIELD}_, message.handles))\n"
86       "    return false;")
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;")
94   setter_template = \
95       Template("  void set_$FIELD($TYPE $FIELD) { ${FIELD}_ = $FIELD; }")
96   ptr_setter_template = \
97       Template("  void set_$FIELD($TYPE $FIELD) { ${FIELD}_.ptr = $FIELD; }")
98   getter_template = \
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];")
103   name_template = \
104       Template("const uint32_t k${INTERFACE}_${METHOD}_Name = $NAME;")
105   templates  = {}
106   HEADER_SIZE = 8
107
108   kind_to_type = {
109     mojom.BOOL:   "bool",
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",
121   }
122
123   @classmethod
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]
134
135   @classmethod
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)
141     if kind.spec == 's':
142       return "mojo::String*"
143     return cls.kind_to_type[kind]
144
145   @classmethod
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)
151     if kind.spec == 's':
152       return "const mojo::String*"
153     return cls.kind_to_type[kind]
154
155   @classmethod
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)
160     else:
161       return cls.getter_template.substitute(subs)
162
163   @classmethod
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)
168     else:
169       return cls.setter_template.substitute(subs)
170
171   @classmethod
172   def GetFieldLine(cls, field):
173     kind = field.kind
174     if kind.spec == 'b':
175       return cls.bool_field_template.substitute(FIELD=field.name)
176     itype = None
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"
183     else:
184       itype = cls.kind_to_type[kind]
185     return cls.field_template.substitute(FIELD=field.name, TYPE=itype)
186
187   @classmethod
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))
191
192     return cls.GetTemplate("interface_stub_case").substitute(
193         CLASS = interface.name,
194         METHOD = method.name,
195         METHOD_CALL = method_call);
196
197   @classmethod
198   def GetSerializedFields(cls, ps):
199     fields = []
200     for pf in ps.packed_fields:
201       if IsPointerKind(pf.field.kind):
202         fields.append(pf.field)
203     return fields
204
205   @classmethod
206   def GetHandleFields(cls, ps):
207     fields = []
208     for pf in ps.packed_fields:
209       if pf.field.kind.spec == 'h':
210         fields.append(pf.field)
211     return fields
212
213   def GetHeaderGuard(self, name):
214     return "MOJO_GENERATED_BINDINGS_%s_%s_H_" % \
215         (self.module.name.upper(), name.upper())
216
217   def GetHeaderFile(self, *components):
218     component_string = '_'.join(components)
219     return os.path.join(
220       self.header_dir,
221       "%s_%s.h" % (self.module.name.lower(), component_string.lower()))
222
223   # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all files
224   # to stdout.
225   def __init__(self, module, header_dir, output_dir=None):
226     self.module = module
227     self.header_dir = header_dir
228     self.output_dir = output_dir
229
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:
238       file = sys.stdout
239     else:
240       file = open(os.path.join(self.output_dir, filename), "w+")
241     try:
242       file.write(template.substitute(substitutions))
243     finally:
244       if self.output_dir is not None:
245         file.close()
246
247   def GetStructDeclaration(self, name, ps):
248     params_template = self.GetTemplate("struct_declaration")
249     fields = []
250     setters = []
251     getters = []
252     pad_count = 0
253     num_fields = len(ps.packed_fields)
254     for i in xrange(num_fields):
255       pf = ps.packed_fields[i]
256       field = pf.field
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)
261         if pad > 0:
262           fields.append(self.pad_template.substitute(COUNT=pad_count, PAD=pad))
263           pad_count += 1
264       setters.append(self.GetSetterLine(field))
265       getters.append(self.GetGetterLine(field))
266
267     if num_fields > 0:
268       last_field = ps.packed_fields[num_fields - 1]
269       offset = last_field.offset + last_field.size
270       pad = mojom_pack.GetPad(offset, 8)
271       if pad > 0:
272         fields.append(self.pad_template.substitute(COUNT=pad_count, PAD=pad))
273         pad_count += 1
274       size = offset + pad
275     else:
276       size = 0
277     return params_template.substitute(
278         CLASS = name,
279         SETTERS = '\n'.join(setters),
280         GETTERS = '\n'.join(getters),
281         FIELDS = '\n'.join(fields),
282         SIZE = size + self.HEADER_SIZE)
283
284   def GetStructImplementation(self, name, ps):
285     return self.GetTemplate("struct_implementation").substitute(
286         CLASS = name,
287         NUM_FIELDS = len(ps.packed_fields))
288
289   def GetStructSerialization(self, class_name, param_name, ps):
290     struct = ps.struct
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)
297     for field in fields:
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),
307         NAME = param_name,
308         ENCODES = encodes,
309         DECODES = decodes,
310         ENCODE_HANDLES = encode_handles,
311         DECODE_HANDLES = decode_handles)
312
313   def GenerateStructHeader(self, ps):
314     struct = ps.struct
315     forwards = Forwards()
316     for field in struct.fields:
317       forwards.Add(field.kind)
318
319     self.WriteTemplateToFile("struct.h", struct.name,
320         HEADER_GUARD = self.GetHeaderGuard(struct.name),
321         CLASS = struct.name.capitalize(),
322         FORWARDS = forwards,
323         DECLARATION = self.GetStructDeclaration(struct.name.capitalize(), ps))
324
325   def GenerateStructSource(self, ps):
326     struct = ps.struct
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),
332         HEADER = header,
333         IMPLEMENTATION = implementation)
334
335   def GenerateStructSerializationHeader(self, ps):
336     struct = ps.struct
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()))
342
343   def GenerateStructSerializationSource(self, ps):
344     struct = ps.struct
345     serialization_header = self.GetHeaderFile(struct.name, "serialization")
346
347     kinds = DependentKinds()
348     for field in struct.fields:
349       kinds.AddKind(field.kind)
350     headers = \
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))
354
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)
359     for field in fields:
360       substitutions = {'NAME': struct.name.lower(), 'FIELD': field.name.lower()}
361       sizes += \
362           self.struct_serialization_compute_template.substitute(substitutions)
363       clones.Add(substitutions)
364     sizes += ";"
365     serialization = \
366         self.GetStructSerialization(struct.name.capitalize(), struct.name, ps)
367     self.WriteTemplateToFile("struct_serialization.cc", struct.name,
368         NAME = struct.name.lower(),
369         CLASS = "%s::%s" % \
370             (self.module.namespace.lower(), struct.name.capitalize()),
371         SERIALIZATION_HEADER = serialization_header,
372         INCLUDES = '\n'.join(includes),
373         SIZES = sizes,
374         CLONES = clones,
375         SERIALIZATION = serialization)
376
377   def GenerateInterfaceHeader(self, interface):
378     methods = []
379     forwards = Forwards()
380     for method in interface.methods:
381       params = []
382       for param in method.parameters:
383         forwards.Add(param.kind)
384         params.append("%s %s" % (self.GetConstType(param.kind), param.name))
385       methods.append(
386           "  virtual void %s(%s) = 0;" % (method.name, ", ".join(params)))
387
388     self.WriteTemplateToFile("interface.h", interface.name,
389         HEADER_GUARD = self.GetHeaderGuard(interface.name),
390         CLASS = interface.name.capitalize(),
391         FORWARDS = forwards,
392         METHODS = '\n'.join(methods))
393
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(),
399         HEADER = header)
400
401   def GenerateInterfaceStubSource(self, interface):
402     stub_header = self.GetHeaderFile(interface.name, "stub")
403     serialization_header = self.GetHeaderFile(interface.name, "serialization")
404     cases = []
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)
412
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)
418     headers = \
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))
423
424     names = []
425     name = 1
426     param_classes = []
427     param_templates = []
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(),
433         NAME = name))
434       name += 1
435
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))
440
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))
447
448   def GenerateInterfaceSerializationSource(self, interface):
449     implementations = []
450     serializations = []
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))
461
462   def GenerateInterfaceProxyHeader(self, interface):
463     methods = []
464     for method in interface.methods:
465       params = map(
466           lambda param: "%s %s" % (self.GetConstType(param.kind), param.name),
467           method.parameters)
468       methods.append(
469           "  virtual void %s(%s) MOJO_OVERRIDE;" \
470               % (method.name, ", ".join(params)))
471
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))
477
478   def GenerateInterfaceProxySource(self, interface):
479     implementations = Lines(self.GetTemplate("proxy_implementation"))
480     for method in interface.methods:
481       sets = []
482       computes = Lines(self.param_struct_compute_template)
483       for param in method.parameters:
484         if IsPointerKind(param.kind):
485           sets.append(
486               self.param_struct_set_template.substitute(NAME=param.name))
487           computes.Add(NAME=param.name)
488         else:
489           sets.append(self.param_set_template.substitute(NAME=param.name))
490       params_list = map(
491           lambda param: "%s %s" % (self.GetConstType(param.kind), param.name),
492           method.parameters)
493       name = \
494           "internal::k%s_%s_Name" % (interface.name.capitalize(), method.name)
495       params_name = \
496           "internal::%s_%s_Params" % (interface.name.capitalize(), method.name)
497
498       implementations.Add(
499           CLASS = interface.name.capitalize(),
500           METHOD = method.name,
501           NAME = name,
502           PARAMS = params_name,
503           PARAMS_LIST = ', '.join(params_list),
504           COMPUTES = computes,
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)
512
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)