1 # Copyright (c) 2012 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 import os 6 7 from code import Code 8 from model import PropertyType 9 import cpp_util 10 import schema_util 11 12 class HGenerator(object): 13 def __init__(self, type_generator): 14 self._type_generator = type_generator 15 16 def Generate(self, namespace): 17 return _Generator(namespace, self._type_generator).Generate() 18 19 20 class _Generator(object): 21 """A .h generator for a namespace. 22 """ 23 def __init__(self, namespace, cpp_type_generator): 24 self._namespace = namespace 25 self._type_helper = cpp_type_generator 26 self._generate_error_messages = namespace.compiler_options.get( 27 'generate_error_messages', False) 28 29 def Generate(self): 30 """Generates a Code object with the .h for a single namespace. 31 """ 32 c = Code() 33 (c.Append(cpp_util.CHROMIUM_LICENSE) 34 .Append() 35 .Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file) 36 .Append() 37 ) 38 39 # Hack: for the purpose of gyp the header file will always be the source 40 # file with its file extension replaced by '.h'. Assume so. 41 output_file = os.path.splitext(self._namespace.source_file)[0] + '.h' 42 ifndef_name = cpp_util.GenerateIfndefName(output_file) 43 44 (c.Append('#ifndef %s' % ifndef_name) 45 .Append('#define %s' % ifndef_name) 46 .Append() 47 .Append('#include <map>') 48 .Append('#include <string>') 49 .Append('#include <vector>') 50 .Append() 51 .Append('#include "base/basictypes.h"') 52 .Append('#include "base/logging.h"') 53 .Append('#include "base/memory/linked_ptr.h"') 54 .Append('#include "base/memory/scoped_ptr.h"') 55 .Append('#include "base/values.h"') 56 .Cblock(self._type_helper.GenerateIncludes()) 57 .Append() 58 ) 59 60 # TODO(calamity): These forward declarations should be #includes to allow 61 # $ref types from other files to be used as required params. This requires 62 # some detangling of windows and tabs which will currently lead to circular 63 # #includes. 64 c.Cblock(self._type_helper.GenerateForwardDeclarations()) 65 66 cpp_namespace = cpp_util.GetCppNamespace( 67 self._namespace.environment.namespace_pattern, 68 self._namespace.unix_name) 69 c.Concat(cpp_util.OpenNamespace(cpp_namespace)) 70 c.Append() 71 if self._namespace.properties: 72 (c.Append('//') 73 .Append('// Properties') 74 .Append('//') 75 .Append() 76 ) 77 for property in self._namespace.properties.values(): 78 property_code = self._type_helper.GeneratePropertyValues( 79 property, 80 'extern const %(type)s %(name)s;') 81 if property_code: 82 c.Cblock(property_code) 83 if self._namespace.types: 84 (c.Append('//') 85 .Append('// Types') 86 .Append('//') 87 .Append() 88 .Cblock(self._GenerateTypes(self._FieldDependencyOrder(), 89 is_toplevel=True, 90 generate_typedefs=True)) 91 ) 92 if self._namespace.functions: 93 (c.Append('//') 94 .Append('// Functions') 95 .Append('//') 96 .Append() 97 ) 98 for function in self._namespace.functions.values(): 99 c.Cblock(self._GenerateFunction(function)) 100 if self._namespace.events: 101 (c.Append('//') 102 .Append('// Events') 103 .Append('//') 104 .Append() 105 ) 106 for event in self._namespace.events.values(): 107 c.Cblock(self._GenerateEvent(event)) 108 (c.Concat(cpp_util.CloseNamespace(cpp_namespace)) 109 .Append('#endif // %s' % ifndef_name) 110 .Append() 111 ) 112 return c 113 114 def _FieldDependencyOrder(self): 115 """Generates the list of types in the current namespace in an order in which 116 depended-upon types appear before types which depend on them. 117 """ 118 dependency_order = [] 119 120 def ExpandType(path, type_): 121 if type_ in path: 122 raise ValueError("Illegal circular dependency via cycle " + 123 ", ".join(map(lambda x: x.name, path + [type_]))) 124 for prop in type_.properties.values(): 125 if (prop.type_ == PropertyType.REF and 126 schema_util.GetNamespace(prop.ref_type) == self._namespace.name): 127 ExpandType(path + [type_], self._namespace.types[prop.ref_type]) 128 if not type_ in dependency_order: 129 dependency_order.append(type_) 130 131 for type_ in self._namespace.types.values(): 132 ExpandType([], type_) 133 return dependency_order 134 135 def _GenerateEnumDeclaration(self, enum_name, type_): 136 """Generate a code object with the declaration of a C++ enum. 137 """ 138 c = Code() 139 c.Sblock('enum %s {' % enum_name) 140 c.Append(self._type_helper.GetEnumNoneValue(type_) + ',') 141 for value in type_.enum_values: 142 current_enum_string = self._type_helper.GetEnumValue(type_, value) 143 c.Append(current_enum_string + ',') 144 c.Append('%s = %s,' % ( 145 self._type_helper.GetEnumLastValue(type_), current_enum_string)) 146 c.Eblock('};') 147 return c 148 149 def _GenerateFields(self, props): 150 """Generates the field declarations when declaring a type. 151 """ 152 c = Code() 153 needs_blank_line = False 154 for prop in props: 155 if needs_blank_line: 156 c.Append() 157 needs_blank_line = True 158 if prop.description: 159 c.Comment(prop.description) 160 # ANY is a base::Value which is abstract and cannot be a direct member, so 161 # we always need to wrap it in a scoped_ptr. 162 is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY 163 (c.Append('%s %s;' % ( 164 self._type_helper.GetCppType(prop.type_, is_ptr=is_ptr), 165 prop.unix_name)) 166 ) 167 return c 168 169 def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False): 170 """Generates a struct for |type_|. 171 172 |is_toplevel| implies that the type was declared in the "types" field 173 of an API schema. This determines the correct function 174 modifier(s). 175 |generate_typedefs| controls whether primitive types should be generated as 176 a typedef. This may not always be desired. If false, 177 primitive types are ignored. 178 """ 179 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 180 c = Code() 181 182 if type_.functions: 183 # Wrap functions within types in the type's namespace. 184 (c.Append('namespace %s {' % classname) 185 .Append() 186 ) 187 for function in type_.functions.values(): 188 c.Cblock(self._GenerateFunction(function)) 189 c.Append('} // namespace %s' % classname) 190 elif type_.property_type == PropertyType.ARRAY: 191 if generate_typedefs and type_.description: 192 c.Comment(type_.description) 193 c.Cblock(self._GenerateType(type_.item_type)) 194 if generate_typedefs: 195 (c.Append('typedef std::vector<%s > %s;' % ( 196 self._type_helper.GetCppType(type_.item_type), 197 classname)) 198 ) 199 elif type_.property_type == PropertyType.STRING: 200 if generate_typedefs: 201 if type_.description: 202 c.Comment(type_.description) 203 c.Append('typedef std::string %(classname)s;') 204 elif type_.property_type == PropertyType.ENUM: 205 if type_.description: 206 c.Comment(type_.description) 207 c.Cblock(self._GenerateEnumDeclaration(classname, type_)); 208 # Top level enums are in a namespace scope so the methods shouldn't be 209 # static. On the other hand, those declared inline (e.g. in an object) do. 210 maybe_static = '' if is_toplevel else 'static ' 211 (c.Append() 212 .Append('%sstd::string ToString(%s as_enum);' % 213 (maybe_static, classname)) 214 .Append('%s%s Parse%s(const std::string& as_string);' % 215 (maybe_static, classname, classname)) 216 ) 217 elif type_.property_type in (PropertyType.CHOICES, 218 PropertyType.OBJECT): 219 if type_.description: 220 c.Comment(type_.description) 221 (c.Sblock('struct %(classname)s {') 222 .Append('%(classname)s();') 223 .Append('~%(classname)s();') 224 ) 225 if type_.origin.from_json: 226 (c.Append() 227 .Comment('Populates a %s object from a base::Value. Returns' 228 ' whether |out| was successfully populated.' % classname) 229 .Append('static bool Populate(%s);' % self._GenerateParams( 230 ('const base::Value& value', '%s* out' % classname))) 231 ) 232 if is_toplevel: 233 (c.Append() 234 .Comment('Creates a %s object from a base::Value, or NULL on ' 235 'failure.' % classname) 236 .Append('static scoped_ptr<%s> FromValue(%s);' % ( 237 classname, self._GenerateParams(('const base::Value& value',)))) 238 ) 239 if type_.origin.from_client: 240 value_type = ('base::Value' 241 if type_.property_type is PropertyType.CHOICES else 242 'base::DictionaryValue') 243 (c.Append() 244 .Comment('Returns a new %s representing the serialized form of this ' 245 '%s object.' % (value_type, classname)) 246 .Append('scoped_ptr<%s> ToValue() const;' % value_type) 247 ) 248 if type_.property_type == PropertyType.CHOICES: 249 # Choices are modelled with optional fields for each choice. Exactly one 250 # field of the choice is guaranteed to be set by the compiler. 251 c.Cblock(self._GenerateTypes(type_.choices)) 252 c.Append('// Choices:') 253 for choice_type in type_.choices: 254 c.Append('%s as_%s;' % ( 255 self._type_helper.GetCppType(choice_type, is_ptr=True), 256 choice_type.unix_name)) 257 else: 258 properties = type_.properties.values() 259 (c.Append() 260 .Cblock(self._GenerateTypes(p.type_ for p in properties)) 261 .Cblock(self._GenerateFields(properties))) 262 if type_.additional_properties is not None: 263 # Most additionalProperties actually have type "any", which is better 264 # modelled as a DictionaryValue rather than a map of string -> Value. 265 if type_.additional_properties.property_type == PropertyType.ANY: 266 c.Append('base::DictionaryValue additional_properties;') 267 else: 268 (c.Cblock(self._GenerateType(type_.additional_properties)) 269 .Append('std::map<std::string, %s> additional_properties;' % 270 cpp_util.PadForGenerics( 271 self._type_helper.GetCppType(type_.additional_properties, 272 is_in_container=True))) 273 ) 274 (c.Eblock() 275 .Append() 276 .Sblock(' private:') 277 .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);') 278 .Eblock('};') 279 ) 280 return c.Substitute({'classname': classname}) 281 282 def _GenerateEvent(self, event): 283 """Generates the namespaces for an event. 284 """ 285 c = Code() 286 # TODO(kalman): use event.unix_name not Classname. 287 event_namespace = cpp_util.Classname(event.name) 288 (c.Append('namespace %s {' % event_namespace) 289 .Append() 290 .Concat(self._GenerateEventNameConstant(event)) 291 .Concat(self._GenerateCreateCallbackArguments(event)) 292 .Eblock('} // namespace %s' % event_namespace) 293 ) 294 return c 295 296 def _GenerateFunction(self, function): 297 """Generates the namespaces and structs for a function. 298 """ 299 c = Code() 300 # TODO(kalman): Use function.unix_name not Classname here. 301 function_namespace = cpp_util.Classname(function.name) 302 # Windows has a #define for SendMessage, so to avoid any issues, we need 303 # to not use the name. 304 if function_namespace == 'SendMessage': 305 function_namespace = 'PassMessage' 306 (c.Append('namespace %s {' % function_namespace) 307 .Append() 308 .Cblock(self._GenerateFunctionParams(function)) 309 ) 310 if function.callback: 311 c.Cblock(self._GenerateFunctionResults(function.callback)) 312 c.Append('} // namespace %s' % function_namespace) 313 return c 314 315 def _GenerateFunctionParams(self, function): 316 """Generates the struct for passing parameters from JSON to a function. 317 """ 318 if not function.params: 319 return Code() 320 321 c = Code() 322 (c.Sblock('struct Params {') 323 .Append('static scoped_ptr<Params> Create(%s);' % self._GenerateParams( 324 ('const base::ListValue& args',))) 325 .Append('~Params();') 326 .Append() 327 .Cblock(self._GenerateTypes(p.type_ for p in function.params)) 328 .Cblock(self._GenerateFields(function.params)) 329 .Eblock() 330 .Append() 331 .Sblock(' private:') 332 .Append('Params();') 333 .Append() 334 .Append('DISALLOW_COPY_AND_ASSIGN(Params);') 335 .Eblock('};') 336 ) 337 return c 338 339 def _GenerateTypes(self, types, is_toplevel=False, generate_typedefs=False): 340 """Generate the structures required by a property such as OBJECT classes 341 and enums. 342 """ 343 c = Code() 344 for type_ in types: 345 c.Cblock(self._GenerateType(type_, 346 is_toplevel=is_toplevel, 347 generate_typedefs=generate_typedefs)) 348 return c 349 350 def _GenerateCreateCallbackArguments(self, function): 351 """Generates functions for passing parameters to a callback. 352 """ 353 c = Code() 354 params = function.params 355 c.Cblock(self._GenerateTypes((p.type_ for p in params), is_toplevel=True)) 356 357 declaration_list = [] 358 for param in params: 359 if param.description: 360 c.Comment(param.description) 361 declaration_list.append(cpp_util.GetParameterDeclaration( 362 param, self._type_helper.GetCppType(param.type_))) 363 c.Append('scoped_ptr<base::ListValue> Create(%s);' % 364 ', '.join(declaration_list)) 365 return c 366 367 def _GenerateEventNameConstant(self, event): 368 """Generates a constant string array for the event name. 369 """ 370 c = Code() 371 c.Append('extern const char kEventName[]; // "%s.%s"' % ( 372 self._namespace.name, event.name)) 373 c.Append() 374 return c 375 376 def _GenerateFunctionResults(self, callback): 377 """Generates namespace for passing a function's result back. 378 """ 379 c = Code() 380 (c.Append('namespace Results {') 381 .Append() 382 .Concat(self._GenerateCreateCallbackArguments(callback)) 383 .Append('} // namespace Results') 384 ) 385 return c 386 387 def _GenerateParams(self, params): 388 """Builds the parameter list for a function, given an array of parameters. 389 """ 390 # |error| is populated with warnings and/or errors found during parsing. 391 # |error| being set does not necessarily imply failure and may be 392 # recoverable. 393 # For example, optional properties may have failed to parse, but the 394 # parser was able to continue. 395 if self._generate_error_messages: 396 params += ('base::string16* error',) 397 return ', '.join(str(p) for p in params) 398