OLD | NEW |
| (Empty) |
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 | |
7 from functools import partial | |
8 from itertools import chain | |
9 import os.path | |
10 import re | |
11 | |
12 import module as mojom | |
13 import mojom.fileutil as fileutil | |
14 import pack | |
15 | |
16 def ExpectedArraySize(kind): | |
17 if mojom.IsArrayKind(kind): | |
18 return kind.length | |
19 return None | |
20 | |
21 def StudlyCapsToCamel(studly): | |
22 return studly[0].lower() + studly[1:] | |
23 | |
24 def CamelCaseToAllCaps(camel_case): | |
25 return '_'.join( | |
26 word for word in re.split(r'([A-Z][^A-Z]+)', camel_case) if word).upper() | |
27 | |
28 def UnderToCamel(under): | |
29 """Converts underscore_separated strings to CamelCase strings.""" | |
30 return ''.join(word.capitalize() for word in under.split('_')) | |
31 | |
32 def WriteFile(contents, full_path): | |
33 # Make sure the containing directory exists. | |
34 full_dir = os.path.dirname(full_path) | |
35 fileutil.EnsureDirectoryExists(full_dir) | |
36 | |
37 # Dump the data to disk. | |
38 with open(full_path, "w+") as f: | |
39 f.write(contents) | |
40 | |
41 class Generator(object): | |
42 # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all | |
43 # files to stdout. | |
44 def __init__(self, module, output_dir=None): | |
45 self.module = module | |
46 self.output_dir = output_dir | |
47 | |
48 def GetStructsFromMethods(self): | |
49 result = [] | |
50 for interface in self.module.interfaces: | |
51 for method in interface.methods: | |
52 result.append(self._GetStructFromMethod(method)) | |
53 if method.response_parameters != None: | |
54 result.append(self._GetResponseStructFromMethod(method)) | |
55 return result | |
56 | |
57 def GetStructs(self): | |
58 return map(partial(self._AddStructComputedData, True), self.module.structs) | |
59 | |
60 def GetUnions(self): | |
61 return self.module.unions | |
62 | |
63 def GetInterfaces(self): | |
64 return map(self._AddInterfaceComputedData, self.module.interfaces) | |
65 | |
66 def GetUsedImports(self, module): | |
67 """GetUsedImports computes the imports that are used in the provided module. | |
68 | |
69 An import being used means that a type or constant defined in the import is | |
70 referenced in the provided module. | |
71 | |
72 Args: | |
73 module: {module.Module} The module whose used imports are to be computed. | |
74 | |
75 Returns: | |
76 {dict<str, dict>} A dictionary of the used imports. The key is the file | |
77 name as defined in the import's Module.path. The value is a dictionary. | |
78 The contents of the dictionary is identical to that found in the | |
79 imported_from field of mojom elements. | |
80 """ | |
81 used = {} | |
82 | |
83 def AddImport(element): | |
84 """AddImport is a utility function that adds the import of the provided | |
85 element to the used dictionary defined above. | |
86 """ | |
87 # Only named values or kinds could be imported. | |
88 if (not isinstance(element, mojom.Kind) and | |
89 not isinstance(element, mojom.NamedValue)): | |
90 return | |
91 | |
92 if mojom.IsArrayKind(element) or mojom.IsInterfaceRequestKind(element): | |
93 AddImport(element.kind) | |
94 return | |
95 if mojom.IsMapKind(element): | |
96 AddImport(element.key_kind) | |
97 AddImport(element.value_kind) | |
98 return | |
99 if not hasattr(element, 'imported_from') or not element.imported_from: | |
100 return | |
101 | |
102 imported_from = element.imported_from | |
103 used[imported_from['module'].path] = imported_from | |
104 | |
105 # We want to collect the structs that represent method input and output | |
106 # parameters. | |
107 all_structs = list(module.structs) | |
108 for interface in module.interfaces: | |
109 for method in interface.methods: | |
110 all_structs.append(self._GetStructFromMethod(method)) | |
111 if method.response_parameters: | |
112 all_structs.append(self._GetResponseStructFromMethod(method)) | |
113 | |
114 for struct in all_structs: | |
115 for field in struct.fields: | |
116 AddImport(field.kind) | |
117 if field.default: | |
118 AddImport(field.default) | |
119 | |
120 # Enums can be defined in the module, in structs or in interfaces. | |
121 enum_containers = [module] + module.structs + module.interfaces | |
122 enums = [c.enums for c in enum_containers] | |
123 for enum in chain.from_iterable(enums): | |
124 for field in enum.fields: | |
125 if field.value: | |
126 AddImport(field.value) | |
127 | |
128 for union in module.unions: | |
129 for field in union.fields: | |
130 AddImport(field.kind) | |
131 | |
132 for constant in module.constants: | |
133 AddImport(constant.value) | |
134 | |
135 return used | |
136 | |
137 # Prepend the filename with a directory that matches the directory of the | |
138 # original .mojom file, relative to the import root. | |
139 def MatchMojomFilePath(self, filename): | |
140 return os.path.join(os.path.dirname(self.module.path), filename) | |
141 | |
142 def Write(self, contents, filename): | |
143 if self.output_dir is None: | |
144 print contents | |
145 return | |
146 full_path = os.path.join(self.output_dir, filename) | |
147 WriteFile(contents, full_path) | |
148 | |
149 def GenerateFiles(self, args): | |
150 raise NotImplementedError("Subclasses must override/implement this method") | |
151 | |
152 def GetJinjaParameters(self): | |
153 """Returns default constructor parameters for the jinja environment.""" | |
154 return {} | |
155 | |
156 def GetGlobals(self): | |
157 """Returns global mappings for the template generation.""" | |
158 return {} | |
159 | |
160 def _AddStructComputedData(self, exported, struct): | |
161 """Adds computed data to the given struct. The data is computed once and | |
162 used repeatedly in the generation process.""" | |
163 if not hasattr(struct, 'packed') or struct.packed is None: | |
164 struct.packed = pack.PackedStruct(struct) | |
165 struct.bytes = pack.GetByteLayout(struct.packed) | |
166 struct.exported = exported | |
167 return struct | |
168 | |
169 def _AddInterfaceComputedData(self, interface): | |
170 """Adds computed data to the given interface. The data is computed once and | |
171 used repeatedly in the generation process.""" | |
172 for method in interface.methods: | |
173 method.param_struct = self._GetStructFromMethod(method) | |
174 if method.response_parameters is not None: | |
175 method.response_param_struct = self._GetResponseStructFromMethod(method) | |
176 else: | |
177 method.response_param_struct = None | |
178 return interface | |
179 | |
180 def _GetStructFromMethod(self, method): | |
181 """Returns a method's parameters as a struct.""" | |
182 return self._AddStructComputedData(False, method.param_struct) | |
183 | |
184 def _GetResponseStructFromMethod(self, method): | |
185 """Returns a method's response_parameters as a struct.""" | |
186 return self._AddStructComputedData(False, method.response_param_struct) | |
OLD | NEW |