| 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 |