summaryrefslogtreecommitdiff
path: root/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
blob: 0e64af78a12b6db2fb6adfc863e081099d87081d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Code shared by the various language-specific code generators."""

from functools import partial
import os.path
import re

import module as mojom
import mojom.fileutil as fileutil
import pack

def ExpectedArraySize(kind):
  if mojom.IsArrayKind(kind):
    return kind.length
  return None

def StudlyCapsToCamel(studly):
  return studly[0].lower() + studly[1:]

def UnderToCamel(under):
  """Converts underscore_separated strings to CamelCase strings."""
  return ''.join(word.capitalize() for word in under.split('_'))

def WriteFile(contents, full_path):
  # Make sure the containing directory exists.
  full_dir = os.path.dirname(full_path)
  fileutil.EnsureDirectoryExists(full_dir)

  # Dump the data to disk.
  with open(full_path, "w+") as f:
    f.write(contents)

class Generator(object):
  # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
  # files to stdout.
  def __init__(self, module, output_dir=None, typemap=None, variant=None,
               bytecode_path=None, for_blink=False, use_once_callback=False,
               use_new_js_bindings=False, export_attribute=None,
               export_header=None, generate_non_variant_code=False):
    self.module = module
    self.output_dir = output_dir
    self.typemap = typemap or {}
    self.variant = variant
    self.bytecode_path = bytecode_path
    self.for_blink = for_blink
    self.use_once_callback = use_once_callback
    self.use_new_js_bindings = use_new_js_bindings
    self.export_attribute = export_attribute
    self.export_header = export_header
    self.generate_non_variant_code = generate_non_variant_code

  def GetStructsFromMethods(self):
    result = []
    for interface in self.module.interfaces:
      for method in interface.methods:
        result.append(self._GetStructFromMethod(method))
        if method.response_parameters != None:
          result.append(self._GetResponseStructFromMethod(method))
    return result

  def GetStructs(self):
    return map(partial(self._AddStructComputedData, True), self.module.structs)

  def GetUnions(self):
    return map(self._AddUnionComputedData, self.module.unions)

  def GetInterfaces(self):
    return map(self._AddInterfaceComputedData, self.module.interfaces)

  # Prepend the filename with a directory that matches the directory of the
  # original .mojom file, relative to the import root.
  def MatchMojomFilePath(self, filename):
    return os.path.join(os.path.dirname(self.module.path), filename)

  def Write(self, contents, filename):
    if self.output_dir is None:
      print contents
      return
    full_path = os.path.join(self.output_dir, filename)
    WriteFile(contents, full_path)

  def GenerateFiles(self, args):
    raise NotImplementedError("Subclasses must override/implement this method")

  def GetJinjaParameters(self):
    """Returns default constructor parameters for the jinja environment."""
    return {}

  def GetGlobals(self):
    """Returns global mappings for the template generation."""
    return {}

  def _AddStructComputedData(self, exported, struct):
    """Adds computed data to the given struct. The data is computed once and
    used repeatedly in the generation process."""
    struct.packed = pack.PackedStruct(struct)
    struct.bytes = pack.GetByteLayout(struct.packed)
    struct.versions = pack.GetVersionInfo(struct.packed)
    struct.exported = exported
    return struct

  def _AddUnionComputedData(self, union):
    """Adds computed data to the given union. The data is computed once and
    used repeatedly in the generation process."""
    ordinal = 0
    for field in union.fields:
      if field.ordinal is not None:
        ordinal = field.ordinal
      field.ordinal = ordinal
      ordinal += 1
    return union

  def _AddInterfaceComputedData(self, interface):
    """Adds computed data to the given interface. The data is computed once and
    used repeatedly in the generation process."""
    interface.version = 0
    for method in interface.methods:
      if method.min_version is not None:
        interface.version = max(interface.version, method.min_version)

      method.param_struct = self._GetStructFromMethod(method)
      interface.version = max(interface.version,
                              method.param_struct.versions[-1].version)

      if method.response_parameters is not None:
        method.response_param_struct = self._GetResponseStructFromMethod(method)
        interface.version = max(
            interface.version,
            method.response_param_struct.versions[-1].version)
      else:
        method.response_param_struct = None
    return interface

  def _GetStructFromMethod(self, method):
    """Converts a method's parameters into the fields of a struct."""
    params_class = "%s_%s_Params" % (method.interface.name, method.name)
    struct = mojom.Struct(params_class, module=method.interface.module)
    for param in method.parameters:
      struct.AddField(param.name, param.kind, param.ordinal,
                      attributes=param.attributes)
    return self._AddStructComputedData(False, struct)

  def _GetResponseStructFromMethod(self, method):
    """Converts a method's response_parameters into the fields of a struct."""
    params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
    struct = mojom.Struct(params_class, module=method.interface.module)
    for param in method.response_parameters:
      struct.AddField(param.name, param.kind, param.ordinal,
                      attributes=param.attributes)
    return self._AddStructComputedData(False, struct)