• 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"""Code shared by the various language-specific code generators."""
6
7from functools import partial
8import os.path
9import re
10
11import module as mojom
12import mojom.fileutil as fileutil
13import pack
14
15
16def ExpectedArraySize(kind):
17  if mojom.IsArrayKind(kind):
18    return kind.length
19  return None
20
21
22def ToCamel(identifier, lower_initial=False, dilimiter='_'):
23  """Splits |identifier| using |dilimiter|, makes the first character of each
24  word uppercased (but makes the first character of the first word lowercased
25  if |lower_initial| is set to True), and joins the words. Please note that for
26  each word, all the characters except the first one are untouched.
27  """
28  result = ''.join(word[0].upper() + word[1:]
29      for word in identifier.split(dilimiter) if word)
30  if lower_initial and result:
31    result = result[0].lower() + result[1:]
32  return result
33
34
35class Stylizer(object):
36  """Stylizers specify naming rules to map mojom names to names in generated
37  code. For example, if you would like method_name in mojom to be mapped to
38  MethodName in the generated code, you need to define a subclass of Stylizer
39  and override StylizeMethod to do the conversion."""
40
41  def StylizeConstant(self, mojom_name):
42    return mojom_name
43
44  def StylizeField(self, mojom_name):
45    return mojom_name
46
47  def StylizeStruct(self, mojom_name):
48    return mojom_name
49
50  def StylizeUnion(self, mojom_name):
51    return mojom_name
52
53  def StylizeParameter(self, mojom_name):
54    return mojom_name
55
56  def StylizeMethod(self, mojom_name):
57    return mojom_name
58
59  def StylizeInterface(self, mojom_name):
60    return mojom_name
61
62  def StylizeEnumField(self, mojom_name):
63    return mojom_name
64
65  def StylizeEnum(self, mojom_name):
66    return mojom_name
67
68  def StylizeModule(self, mojom_namespace):
69    return mojom_namespace
70
71
72def WriteFile(contents, full_path):
73  # If |contents| is same with the file content, we skip updating.
74  if os.path.isfile(full_path):
75    with open(full_path, 'rb') as destination_file:
76      if destination_file.read() == contents:
77        return
78
79  # Make sure the containing directory exists.
80  full_dir = os.path.dirname(full_path)
81  fileutil.EnsureDirectoryExists(full_dir)
82
83  # Dump the data to disk.
84  with open(full_path, "wb") as f:
85    f.write(contents)
86
87
88def AddComputedData(module):
89  """Adds computed data to the given module. The data is computed once and
90  used repeatedly in the generation process."""
91
92  def _AddStructComputedData(exported, struct):
93    struct.packed = pack.PackedStruct(struct)
94    struct.bytes = pack.GetByteLayout(struct.packed)
95    struct.versions = pack.GetVersionInfo(struct.packed)
96    struct.exported = exported
97
98  def _AddUnionComputedData(union):
99    ordinal = 0
100    for field in union.fields:
101      if field.ordinal is not None:
102        ordinal = field.ordinal
103      field.ordinal = ordinal
104      ordinal += 1
105
106  def _AddInterfaceComputedData(interface):
107    next_ordinal = 0
108    interface.version = 0
109    for method in interface.methods:
110      if method.ordinal is None:
111        method.ordinal = next_ordinal
112      next_ordinal = method.ordinal + 1
113
114      if method.min_version is not None:
115        interface.version = max(interface.version, method.min_version)
116
117      method.param_struct = _GetStructFromMethod(method)
118      interface.version = max(interface.version,
119                              method.param_struct.versions[-1].version)
120
121      if method.response_parameters is not None:
122        method.response_param_struct = _GetResponseStructFromMethod(method)
123        interface.version = max(
124            interface.version,
125            method.response_param_struct.versions[-1].version)
126      else:
127        method.response_param_struct = None
128
129  def _GetStructFromMethod(method):
130    """Converts a method's parameters into the fields of a struct."""
131    params_class = "%s_%s_Params" % (method.interface.mojom_name,
132                                     method.mojom_name)
133    struct = mojom.Struct(params_class, module=method.interface.module)
134    for param in method.parameters:
135      struct.AddField(param.mojom_name, param.kind, param.ordinal,
136                      attributes=param.attributes)
137    _AddStructComputedData(False, struct)
138    return struct
139
140  def _GetResponseStructFromMethod(method):
141    """Converts a method's response_parameters into the fields of a struct."""
142    params_class = "%s_%s_ResponseParams" % (method.interface.mojom_name,
143                                             method.mojom_name)
144    struct = mojom.Struct(params_class, module=method.interface.module)
145    for param in method.response_parameters:
146      struct.AddField(param.mojom_name, param.kind, param.ordinal,
147                      attributes=param.attributes)
148    _AddStructComputedData(False, struct)
149    return struct
150
151  for struct in module.structs:
152    _AddStructComputedData(True, struct)
153  for union in module.unions:
154    _AddUnionComputedData(union)
155  for interface in module.interfaces:
156    _AddInterfaceComputedData(interface)
157
158
159class Generator(object):
160  # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
161  # files to stdout.
162  def __init__(self, module, output_dir=None, typemap=None, variant=None,
163               bytecode_path=None, for_blink=False, use_once_callback=False,
164               js_bindings_mode="new", export_attribute=None,
165               export_header=None, generate_non_variant_code=False,
166               support_lazy_serialization=False, disallow_native_types=False,
167               disallow_interfaces=False, generate_message_ids=False,
168               generate_fuzzing=False):
169    self.module = module
170    self.output_dir = output_dir
171    self.typemap = typemap or {}
172    self.variant = variant
173    self.bytecode_path = bytecode_path
174    self.for_blink = for_blink
175    self.use_once_callback = use_once_callback
176    self.js_bindings_mode = js_bindings_mode
177    self.export_attribute = export_attribute
178    self.export_header = export_header
179    self.generate_non_variant_code = generate_non_variant_code
180    self.support_lazy_serialization = support_lazy_serialization
181    self.disallow_native_types = disallow_native_types
182    self.disallow_interfaces = disallow_interfaces
183    self.generate_message_ids = generate_message_ids
184    self.generate_fuzzing = generate_fuzzing
185
186  def Write(self, contents, filename):
187    if self.output_dir is None:
188      print contents
189      return
190    full_path = os.path.join(self.output_dir, filename)
191    WriteFile(contents, full_path)
192
193  def GenerateFiles(self, args):
194    raise NotImplementedError("Subclasses must override/implement this method")
195
196  def GetJinjaParameters(self):
197    """Returns default constructor parameters for the jinja environment."""
198    return {}
199
200  def GetGlobals(self):
201    """Returns global mappings for the template generation."""
202    return {}
203