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 import os.path 9 import re 10 11 import module as mojom 12 import mojom.fileutil as fileutil 13 import pack 14 15 def ExpectedArraySize(kind): 16 if mojom.IsArrayKind(kind): 17 return kind.length 18 return None 19 20 def StudlyCapsToCamel(studly): 21 return studly[0].lower() + studly[1:] 22 23 def UnderToCamel(under): 24 """Converts underscore_separated strings to CamelCase strings.""" 25 return ''.join(word.capitalize() for word in under.split('_')) 26 27 def WriteFile(contents, full_path): 28 # Make sure the containing directory exists. 29 full_dir = os.path.dirname(full_path) 30 fileutil.EnsureDirectoryExists(full_dir) 31 32 # Dump the data to disk. 33 with open(full_path, "w+") as f: 34 f.write(contents) 35 36 class Generator(object): 37 # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all 38 # files to stdout. 39 def __init__(self, module, output_dir=None, typemap=None, variant=None, 40 bytecode_path=None, for_blink=False, use_once_callback=False, 41 export_attribute=None, export_header=None, 42 generate_non_variant_code=False): 43 self.module = module 44 self.output_dir = output_dir 45 self.typemap = typemap or {} 46 self.variant = variant 47 self.bytecode_path = bytecode_path 48 self.for_blink = for_blink 49 self.use_once_callback = use_once_callback 50 self.export_attribute = export_attribute 51 self.export_header = export_header 52 self.generate_non_variant_code = generate_non_variant_code 53 54 def GetStructsFromMethods(self): 55 result = [] 56 for interface in self.module.interfaces: 57 for method in interface.methods: 58 result.append(self._GetStructFromMethod(method)) 59 if method.response_parameters != None: 60 result.append(self._GetResponseStructFromMethod(method)) 61 return result 62 63 def GetStructs(self): 64 return map(partial(self._AddStructComputedData, True), self.module.structs) 65 66 def GetUnions(self): 67 return map(self._AddUnionComputedData, self.module.unions) 68 69 def GetInterfaces(self): 70 return map(self._AddInterfaceComputedData, self.module.interfaces) 71 72 # Prepend the filename with a directory that matches the directory of the 73 # original .mojom file, relative to the import root. 74 def MatchMojomFilePath(self, filename): 75 return os.path.join(os.path.dirname(self.module.path), filename) 76 77 def Write(self, contents, filename): 78 if self.output_dir is None: 79 print contents 80 return 81 full_path = os.path.join(self.output_dir, filename) 82 WriteFile(contents, full_path) 83 84 def GenerateFiles(self, args): 85 raise NotImplementedError("Subclasses must override/implement this method") 86 87 def GetJinjaParameters(self): 88 """Returns default constructor parameters for the jinja environment.""" 89 return {} 90 91 def GetGlobals(self): 92 """Returns global mappings for the template generation.""" 93 return {} 94 95 def _AddStructComputedData(self, exported, struct): 96 """Adds computed data to the given struct. The data is computed once and 97 used repeatedly in the generation process.""" 98 struct.packed = pack.PackedStruct(struct) 99 struct.bytes = pack.GetByteLayout(struct.packed) 100 struct.versions = pack.GetVersionInfo(struct.packed) 101 struct.exported = exported 102 return struct 103 104 def _AddUnionComputedData(self, union): 105 """Adds computed data to the given union. The data is computed once and 106 used repeatedly in the generation process.""" 107 ordinal = 0 108 for field in union.fields: 109 if field.ordinal is not None: 110 ordinal = field.ordinal 111 field.ordinal = ordinal 112 ordinal += 1 113 return union 114 115 def _AddInterfaceComputedData(self, interface): 116 """Adds computed data to the given interface. The data is computed once and 117 used repeatedly in the generation process.""" 118 interface.version = 0 119 for method in interface.methods: 120 if method.min_version is not None: 121 interface.version = max(interface.version, method.min_version) 122 123 method.param_struct = self._GetStructFromMethod(method) 124 interface.version = max(interface.version, 125 method.param_struct.versions[-1].version) 126 127 if method.response_parameters is not None: 128 method.response_param_struct = self._GetResponseStructFromMethod(method) 129 interface.version = max( 130 interface.version, 131 method.response_param_struct.versions[-1].version) 132 else: 133 method.response_param_struct = None 134 return interface 135 136 def _GetStructFromMethod(self, method): 137 """Converts a method's parameters into the fields of a struct.""" 138 params_class = "%s_%s_Params" % (method.interface.name, method.name) 139 struct = mojom.Struct(params_class, module=method.interface.module) 140 for param in method.parameters: 141 struct.AddField(param.name, param.kind, param.ordinal, 142 attributes=param.attributes) 143 return self._AddStructComputedData(False, struct) 144 145 def _GetResponseStructFromMethod(self, method): 146 """Converts a method's response_parameters into the fields of a struct.""" 147 params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name) 148 struct = mojom.Struct(params_class, module=method.interface.module) 149 for param in method.response_parameters: 150 struct.AddField(param.name, param.kind, param.ordinal, 151 attributes=param.attributes) 152 return self._AddStructComputedData(False, struct) 153