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