cc_generator.py revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 5from code import Code 6from model import PropertyType 7import cpp_util 8import schema_util 9import util_cc_helper 10 11class CCGenerator(object): 12 def __init__(self, type_generator, cpp_namespace): 13 self._type_generator = type_generator 14 self._cpp_namespace = cpp_namespace 15 16 def Generate(self, namespace): 17 return _Generator(namespace, 18 self._type_generator, 19 self._cpp_namespace).Generate() 20 21 22class _Generator(object): 23 """A .cc generator for a namespace. 24 """ 25 def __init__(self, namespace, cpp_type_generator, cpp_namespace): 26 self._namespace = namespace 27 self._type_helper = cpp_type_generator 28 self._cpp_namespace = cpp_namespace 29 self._target_namespace = ( 30 self._type_helper.GetCppNamespaceName(self._namespace)) 31 self._util_cc_helper = ( 32 util_cc_helper.UtilCCHelper(self._type_helper)) 33 self._generate_error_messages = namespace.compiler_options.get( 34 'generate_error_messages', False) 35 36 def Generate(self): 37 """Generates a Code object with the .cc for a single namespace. 38 """ 39 c = Code() 40 (c.Append(cpp_util.CHROMIUM_LICENSE) 41 .Append() 42 .Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file) 43 .Append() 44 .Append(self._util_cc_helper.GetIncludePath()) 45 .Append('#include "base/logging.h"') 46 .Append('#include "base/strings/string_number_conversions.h"') 47 .Append('#include "base/strings/utf_string_conversions.h"') 48 .Append('#include "%s/%s.h"' % 49 (self._namespace.source_file_dir, self._namespace.short_filename)) 50 .Append('#include <set>') 51 .Cblock(self._type_helper.GenerateIncludes(include_soft=True)) 52 .Append() 53 .Append('using base::UTF8ToUTF16;') 54 .Append() 55 .Concat(cpp_util.OpenNamespace(self._cpp_namespace)) 56 .Cblock(self._type_helper.GetNamespaceStart()) 57 ) 58 if self._namespace.properties: 59 (c.Append('//') 60 .Append('// Properties') 61 .Append('//') 62 .Append() 63 ) 64 for property in self._namespace.properties.values(): 65 property_code = self._type_helper.GeneratePropertyValues( 66 property, 67 'const %(type)s %(name)s = %(value)s;', 68 nodoc=True) 69 if property_code: 70 c.Cblock(property_code) 71 if self._namespace.types: 72 (c.Append('//') 73 .Append('// Types') 74 .Append('//') 75 .Append() 76 .Cblock(self._GenerateTypes(None, self._namespace.types.values())) 77 ) 78 if self._namespace.functions: 79 (c.Append('//') 80 .Append('// Functions') 81 .Append('//') 82 .Append() 83 ) 84 for function in self._namespace.functions.values(): 85 c.Cblock(self._GenerateFunction(function)) 86 if self._namespace.events: 87 (c.Append('//') 88 .Append('// Events') 89 .Append('//') 90 .Append() 91 ) 92 for event in self._namespace.events.values(): 93 c.Cblock(self._GenerateEvent(event)) 94 (c.Concat(self._type_helper.GetNamespaceEnd()) 95 .Cblock(cpp_util.CloseNamespace(self._cpp_namespace)) 96 ) 97 c.Append() 98 return c 99 100 def _GenerateType(self, cpp_namespace, type_): 101 """Generates the function definitions for a type. 102 """ 103 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 104 c = Code() 105 106 if type_.functions: 107 # Wrap functions within types in the type's namespace. 108 (c.Append('namespace %s {' % classname) 109 .Append()) 110 for function in type_.functions.values(): 111 c.Cblock(self._GenerateFunction(function)) 112 c.Append('} // namespace %s' % classname) 113 elif type_.property_type == PropertyType.ARRAY: 114 c.Cblock(self._GenerateType(cpp_namespace, type_.item_type)) 115 elif type_.property_type in (PropertyType.CHOICES, 116 PropertyType.OBJECT): 117 if cpp_namespace is None: 118 classname_in_namespace = classname 119 else: 120 classname_in_namespace = '%s::%s' % (cpp_namespace, classname) 121 122 if type_.property_type == PropertyType.OBJECT: 123 c.Cblock(self._GeneratePropertyFunctions(classname_in_namespace, 124 type_.properties.values())) 125 else: 126 c.Cblock(self._GenerateTypes(classname_in_namespace, type_.choices)) 127 128 (c.Append('%s::%s()' % (classname_in_namespace, classname)) 129 .Cblock(self._GenerateInitializersAndBody(type_)) 130 .Append('%s::~%s() {}' % (classname_in_namespace, classname)) 131 .Append() 132 ) 133 if type_.origin.from_json: 134 c.Cblock(self._GenerateTypePopulate(classname_in_namespace, type_)) 135 if cpp_namespace is None: # only generate for top-level types 136 c.Cblock(self._GenerateTypeFromValue(classname_in_namespace, type_)) 137 if type_.origin.from_client: 138 c.Cblock(self._GenerateTypeToValue(classname_in_namespace, type_)) 139 elif type_.property_type == PropertyType.ENUM: 140 (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_)) 141 .Cblock(self._GenerateEnumFromString(cpp_namespace, type_)) 142 ) 143 144 return c 145 146 def _GenerateInitializersAndBody(self, type_): 147 items = [] 148 for prop in type_.properties.values(): 149 if prop.optional: 150 continue 151 152 t = prop.type_ 153 if t.property_type == PropertyType.INTEGER: 154 items.append('%s(0)' % prop.unix_name) 155 elif t.property_type == PropertyType.DOUBLE: 156 items.append('%s(0.0)' % prop.unix_name) 157 elif t.property_type == PropertyType.BOOLEAN: 158 items.append('%s(false)' % prop.unix_name) 159 elif (t.property_type == PropertyType.ANY or 160 t.property_type == PropertyType.ARRAY or 161 t.property_type == PropertyType.BINARY or # mapped to std::string 162 t.property_type == PropertyType.CHOICES or 163 t.property_type == PropertyType.ENUM or 164 t.property_type == PropertyType.OBJECT or 165 t.property_type == PropertyType.FUNCTION or 166 t.property_type == PropertyType.REF or 167 t.property_type == PropertyType.STRING): 168 # TODO(miket): It would be nice to initialize CHOICES and ENUM, but we 169 # don't presently have the semantics to indicate which one of a set 170 # should be the default. 171 continue 172 else: 173 raise TypeError(t) 174 175 if items: 176 s = ': %s' % (', '.join(items)) 177 else: 178 s = '' 179 s = s + ' {}' 180 return Code().Append(s) 181 182 def _GenerateTypePopulate(self, cpp_namespace, type_): 183 """Generates the function for populating a type given a pointer to it. 184 185 E.g for type "Foo", generates Foo::Populate() 186 """ 187 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 188 c = Code() 189 (c.Append('// static') 190 .Append('bool %(namespace)s::Populate(') 191 .Sblock(' %s) {' % self._GenerateParams( 192 ('const base::Value& value', '%(name)s* out')))) 193 194 if self._generate_error_messages: 195 c.Append('DCHECK(error);') 196 197 if type_.property_type == PropertyType.CHOICES: 198 for choice in type_.choices: 199 (c.Sblock('if (%s) {' % self._GenerateValueIsTypeExpression('value', 200 choice)) 201 .Concat(self._GeneratePopulateVariableFromValue( 202 choice, 203 '(&value)', 204 'out->as_%s' % choice.unix_name, 205 'false', 206 is_ptr=True)) 207 .Append('return true;') 208 .Eblock('}') 209 ) 210 (c.Concat(self._GenerateError( 211 '"expected %s, got " + %s' % 212 (" or ".join(choice.name for choice in type_.choices), 213 self._util_cc_helper.GetValueTypeString('value')))) 214 .Append('return false;')) 215 elif type_.property_type == PropertyType.OBJECT: 216 (c.Sblock('if (!value.IsType(base::Value::TYPE_DICTIONARY)) {') 217 .Concat(self._GenerateError( 218 '"expected dictionary, got " + ' + 219 self._util_cc_helper.GetValueTypeString('value'))) 220 .Append('return false;') 221 .Eblock('}')) 222 223 if type_.properties or type_.additional_properties is not None: 224 c.Append('const base::DictionaryValue* dict = ' 225 'static_cast<const base::DictionaryValue*>(&value);') 226 if self._generate_error_messages: 227 c.Append('std::set<std::string> keys;') 228 for prop in type_.properties.itervalues(): 229 c.Concat(self._InitializePropertyToDefault(prop, 'out')) 230 for prop in type_.properties.itervalues(): 231 if self._generate_error_messages: 232 c.Append('keys.insert("%s");' % (prop.name)) 233 c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) 234 # Check for extra values. 235 if self._generate_error_messages: 236 (c.Sblock('for (base::DictionaryValue::Iterator it(*dict); ' 237 '!it.IsAtEnd(); it.Advance()) {') 238 .Sblock('if (!keys.count(it.key())) {') 239 .Concat(self._GenerateError('"found unexpected key \'" + ' 240 'it.key() + "\'"')) 241 .Eblock('}') 242 .Eblock('}') 243 ) 244 if type_.additional_properties is not None: 245 if type_.additional_properties.property_type == PropertyType.ANY: 246 c.Append('out->additional_properties.MergeDictionary(dict);') 247 else: 248 cpp_type = self._type_helper.GetCppType(type_.additional_properties, 249 is_in_container=True) 250 (c.Append('for (base::DictionaryValue::Iterator it(*dict);') 251 .Sblock(' !it.IsAtEnd(); it.Advance()) {') 252 .Append('%s tmp;' % cpp_type) 253 .Concat(self._GeneratePopulateVariableFromValue( 254 type_.additional_properties, 255 '(&it.value())', 256 'tmp', 257 'false')) 258 .Append('out->additional_properties[it.key()] = tmp;') 259 .Eblock('}') 260 ) 261 c.Append('return true;') 262 (c.Eblock('}') 263 .Substitute({'namespace': cpp_namespace, 'name': classname})) 264 return c 265 266 def _GenerateValueIsTypeExpression(self, var, type_): 267 real_type = self._type_helper.FollowRef(type_) 268 if real_type.property_type is PropertyType.CHOICES: 269 return '(%s)' % ' || '.join(self._GenerateValueIsTypeExpression(var, 270 choice) 271 for choice in real_type.choices) 272 return '%s.IsType(%s)' % (var, cpp_util.GetValueType(real_type)) 273 274 def _GenerateTypePopulateProperty(self, prop, src, dst): 275 """Generate the code to populate a single property in a type. 276 277 src: base::DictionaryValue* 278 dst: Type* 279 """ 280 c = Code() 281 value_var = prop.unix_name + '_value' 282 c.Append('const base::Value* %(value_var)s = NULL;') 283 if prop.optional: 284 (c.Sblock( 285 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {') 286 .Concat(self._GeneratePopulatePropertyFromValue( 287 prop, value_var, dst, 'false'))) 288 underlying_type = self._type_helper.FollowRef(prop.type_) 289 if underlying_type.property_type == PropertyType.ENUM: 290 (c.Append('} else {') 291 .Append('%%(dst)s->%%(name)s = %s;' % 292 self._type_helper.GetEnumNoneValue(prop.type_))) 293 c.Eblock('}') 294 else: 295 (c.Sblock( 296 'if (!%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {') 297 .Concat(self._GenerateError('"\'%%(key)s\' is required"')) 298 .Append('return false;') 299 .Eblock('}') 300 .Concat(self._GeneratePopulatePropertyFromValue( 301 prop, value_var, dst, 'false')) 302 ) 303 c.Append() 304 c.Substitute({ 305 'value_var': value_var, 306 'key': prop.name, 307 'src': src, 308 'dst': dst, 309 'name': prop.unix_name 310 }) 311 return c 312 313 def _GenerateTypeFromValue(self, cpp_namespace, type_): 314 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 315 c = Code() 316 (c.Append('// static') 317 .Append('scoped_ptr<%s> %s::FromValue(%s) {' % (classname, 318 cpp_namespace, self._GenerateParams(('const base::Value& value',)))) 319 ) 320 if self._generate_error_messages: 321 c.Append('DCHECK(error);') 322 (c.Append(' scoped_ptr<%s> out(new %s());' % (classname, classname)) 323 .Append(' if (!Populate(%s))' % self._GenerateArgs( 324 ('value', 'out.get()'))) 325 .Append(' return scoped_ptr<%s>();' % classname) 326 .Append(' return out.Pass();') 327 .Append('}') 328 ) 329 return c 330 331 def _GenerateTypeToValue(self, cpp_namespace, type_): 332 """Generates a function that serializes the type into a base::Value. 333 E.g. for type "Foo" generates Foo::ToValue() 334 """ 335 if type_.property_type == PropertyType.OBJECT: 336 return self._GenerateObjectTypeToValue(cpp_namespace, type_) 337 elif type_.property_type == PropertyType.CHOICES: 338 return self._GenerateChoiceTypeToValue(cpp_namespace, type_) 339 else: 340 raise ValueError("Unsupported property type %s" % type_.type_) 341 342 def _GenerateObjectTypeToValue(self, cpp_namespace, type_): 343 """Generates a function that serializes an object-representing type 344 into a base::DictionaryValue. 345 """ 346 c = Code() 347 (c.Sblock('scoped_ptr<base::DictionaryValue> %s::ToValue() const {' % 348 cpp_namespace) 349 .Append('scoped_ptr<base::DictionaryValue> value(' 350 'new base::DictionaryValue());') 351 .Append() 352 ) 353 354 for prop in type_.properties.values(): 355 prop_var = 'this->%s' % prop.unix_name 356 if prop.optional: 357 # Optional enum values are generated with a NONE enum value. 358 underlying_type = self._type_helper.FollowRef(prop.type_) 359 if underlying_type.property_type == PropertyType.ENUM: 360 c.Sblock('if (%s != %s) {' % 361 (prop_var, 362 self._type_helper.GetEnumNoneValue(prop.type_))) 363 else: 364 c.Sblock('if (%s.get()) {' % prop_var) 365 366 # ANY is a base::Value which is abstract and cannot be a direct member, so 367 # it will always be a pointer. 368 is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY 369 c.Cblock(self._CreateValueFromType( 370 'value->SetWithoutPathExpansion("%s", %%s);' % prop.name, 371 prop.name, 372 prop.type_, 373 prop_var, 374 is_ptr=is_ptr)) 375 376 if prop.optional: 377 c.Eblock('}') 378 379 if type_.additional_properties is not None: 380 if type_.additional_properties.property_type == PropertyType.ANY: 381 c.Append('value->MergeDictionary(&additional_properties);') 382 else: 383 # Non-copyable types will be wrapped in a linked_ptr for inclusion in 384 # maps, so we need to unwrap them. 385 needs_unwrap = ( 386 not self._type_helper.IsCopyable(type_.additional_properties)) 387 cpp_type = self._type_helper.GetCppType(type_.additional_properties, 388 is_in_container=True) 389 (c.Sblock('for (std::map<std::string, %s>::const_iterator it =' % 390 cpp_util.PadForGenerics(cpp_type)) 391 .Append(' additional_properties.begin();') 392 .Append(' it != additional_properties.end(); ++it) {') 393 .Cblock(self._CreateValueFromType( 394 'value->SetWithoutPathExpansion(it->first, %s);', 395 type_.additional_properties.name, 396 type_.additional_properties, 397 '%sit->second' % ('*' if needs_unwrap else ''))) 398 .Eblock('}') 399 ) 400 401 return (c.Append() 402 .Append('return value.Pass();') 403 .Eblock('}')) 404 405 def _GenerateChoiceTypeToValue(self, cpp_namespace, type_): 406 """Generates a function that serializes a choice-representing type 407 into a base::Value. 408 """ 409 c = Code() 410 c.Sblock('scoped_ptr<base::Value> %s::ToValue() const {' % cpp_namespace) 411 c.Append('scoped_ptr<base::Value> result;') 412 for choice in type_.choices: 413 choice_var = 'as_%s' % choice.unix_name 414 (c.Sblock('if (%s) {' % choice_var) 415 .Append('DCHECK(!result) << "Cannot set multiple choices for %s";' % 416 type_.unix_name) 417 .Cblock(self._CreateValueFromType('result.reset(%s);', 418 choice.name, 419 choice, 420 '*%s' % choice_var)) 421 .Eblock('}') 422 ) 423 (c.Append('DCHECK(result) << "Must set at least one choice for %s";' % 424 type_.unix_name) 425 .Append('return result.Pass();') 426 .Eblock('}') 427 ) 428 return c 429 430 def _GenerateFunction(self, function): 431 """Generates the definitions for function structs. 432 """ 433 c = Code() 434 435 # TODO(kalman): use function.unix_name not Classname. 436 function_namespace = cpp_util.Classname(function.name) 437 # Windows has a #define for SendMessage, so to avoid any issues, we need 438 # to not use the name. 439 if function_namespace == 'SendMessage': 440 function_namespace = 'PassMessage' 441 (c.Append('namespace %s {' % function_namespace) 442 .Append() 443 ) 444 445 # Params::Populate function 446 if function.params: 447 c.Concat(self._GeneratePropertyFunctions('Params', function.params)) 448 (c.Append('Params::Params() {}') 449 .Append('Params::~Params() {}') 450 .Append() 451 .Cblock(self._GenerateFunctionParamsCreate(function)) 452 ) 453 454 # Results::Create function 455 if function.callback: 456 c.Concat(self._GenerateCreateCallbackArguments(function_namespace, 457 'Results', 458 function.callback)) 459 460 c.Append('} // namespace %s' % function_namespace) 461 return c 462 463 def _GenerateEvent(self, event): 464 # TODO(kalman): use event.unix_name not Classname. 465 c = Code() 466 event_namespace = cpp_util.Classname(event.name) 467 (c.Append('namespace %s {' % event_namespace) 468 .Append() 469 .Cblock(self._GenerateEventNameConstant(None, event)) 470 .Cblock(self._GenerateCreateCallbackArguments(event_namespace, 471 None, 472 event)) 473 .Append('} // namespace %s' % event_namespace) 474 ) 475 return c 476 477 def _CreateValueFromType(self, code, prop_name, type_, var, is_ptr=False): 478 """Creates a base::Value given a type. Generated code passes ownership 479 to caller. 480 481 var: variable or variable* 482 483 E.g for std::string, generate new base::StringValue(var) 484 """ 485 c = Code() 486 underlying_type = self._type_helper.FollowRef(type_) 487 if underlying_type.property_type == PropertyType.ARRAY: 488 # Enums are treated specially because C++ templating thinks that they're 489 # ints, but really they're strings. So we create a vector of strings and 490 # populate it with the names of the enum in the array. The |ToString| 491 # function of the enum can be in another namespace when the enum is 492 # referenced. Templates can not be used here because C++ templating does 493 # not support passing a namespace as an argument. 494 item_type = self._type_helper.FollowRef(underlying_type.item_type) 495 if item_type.property_type == PropertyType.ENUM: 496 vardot = '(%s)%s' % (var, '->' if is_ptr else '.') 497 498 maybe_namespace = '' 499 if type_.item_type.property_type == PropertyType.REF: 500 maybe_namespace = '%s::' % item_type.namespace.unix_name 501 502 enum_list_var = '%s_list' % prop_name 503 # Scope the std::vector variable declaration inside braces. 504 (c.Sblock('{') 505 .Append('std::vector<std::string> %s;' % enum_list_var) 506 .Append('for (std::vector<%s>::const_iterator it = %sbegin();' 507 % (self._type_helper.GetCppType(item_type), vardot)) 508 .Sblock(' it != %send(); ++it) {' % vardot) 509 .Append('%s.push_back(%sToString(*it));' % (enum_list_var, 510 maybe_namespace)) 511 .Eblock('}')) 512 513 # Because the std::vector above is always created for both required and 514 # optional enum arrays, |is_ptr| is set to false and uses the 515 # std::vector to create the values. 516 (c.Append(code % 517 self._GenerateCreateValueFromType(type_, enum_list_var, False)) 518 .Eblock('}')) 519 return c 520 521 c.Append(code % self._GenerateCreateValueFromType(type_, var, is_ptr)) 522 return c 523 524 def _GenerateCreateValueFromType(self, type_, var, is_ptr): 525 """Generates the statement to create a base::Value given a type. 526 527 type_: The type of the values being converted. 528 var: The name of the variable. 529 is_ptr: Whether |type_| is optional. 530 """ 531 underlying_type = self._type_helper.FollowRef(type_) 532 if (underlying_type.property_type == PropertyType.CHOICES or 533 underlying_type.property_type == PropertyType.OBJECT): 534 if is_ptr: 535 return '(%s)->ToValue().release()' % var 536 else: 537 return '(%s).ToValue().release()' % var 538 elif (underlying_type.property_type == PropertyType.ANY or 539 underlying_type.property_type == PropertyType.FUNCTION): 540 if is_ptr: 541 vardot = '(%s)->' % var 542 else: 543 vardot = '(%s).' % var 544 return '%sDeepCopy()' % vardot 545 elif underlying_type.property_type == PropertyType.ENUM: 546 maybe_namespace = '' 547 if type_.property_type == PropertyType.REF: 548 maybe_namespace = '%s::' % underlying_type.namespace.unix_name 549 return 'new base::StringValue(%sToString(%s))' % (maybe_namespace, var) 550 elif underlying_type.property_type == PropertyType.BINARY: 551 if is_ptr: 552 vardot = var + '->' 553 else: 554 vardot = var + '.' 555 return ('base::BinaryValue::CreateWithCopiedBuffer(%sdata(), %ssize())' % 556 (vardot, vardot)) 557 elif underlying_type.property_type == PropertyType.ARRAY: 558 return '%s.release()' % self._util_cc_helper.CreateValueFromArray( 559 var, 560 is_ptr) 561 elif underlying_type.property_type.is_fundamental: 562 if is_ptr: 563 var = '*%s' % var 564 if underlying_type.property_type == PropertyType.STRING: 565 return 'new base::StringValue(%s)' % var 566 else: 567 return 'new base::FundamentalValue(%s)' % var 568 else: 569 raise NotImplementedError('Conversion of %s to base::Value not ' 570 'implemented' % repr(type_.type_)) 571 572 def _GenerateParamsCheck(self, function, var): 573 """Generates a check for the correct number of arguments when creating 574 Params. 575 """ 576 c = Code() 577 num_required = 0 578 for param in function.params: 579 if not param.optional: 580 num_required += 1 581 if num_required == len(function.params): 582 c.Sblock('if (%(var)s.GetSize() != %(total)d) {') 583 elif not num_required: 584 c.Sblock('if (%(var)s.GetSize() > %(total)d) {') 585 else: 586 c.Sblock('if (%(var)s.GetSize() < %(required)d' 587 ' || %(var)s.GetSize() > %(total)d) {') 588 (c.Concat(self._GenerateError( 589 '"expected %%(total)d arguments, got " ' 590 '+ base::IntToString(%%(var)s.GetSize())')) 591 .Append('return scoped_ptr<Params>();') 592 .Eblock('}') 593 .Substitute({ 594 'var': var, 595 'required': num_required, 596 'total': len(function.params), 597 })) 598 return c 599 600 def _GenerateFunctionParamsCreate(self, function): 601 """Generate function to create an instance of Params. The generated 602 function takes a base::ListValue of arguments. 603 604 E.g for function "Bar", generate Bar::Params::Create() 605 """ 606 c = Code() 607 (c.Append('// static') 608 .Sblock('scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams( 609 ['const base::ListValue& args'])) 610 ) 611 if self._generate_error_messages: 612 c.Append('DCHECK(error);') 613 (c.Concat(self._GenerateParamsCheck(function, 'args')) 614 .Append('scoped_ptr<Params> params(new Params());') 615 ) 616 617 for param in function.params: 618 c.Concat(self._InitializePropertyToDefault(param, 'params')) 619 620 for i, param in enumerate(function.params): 621 # Any failure will cause this function to return. If any argument is 622 # incorrect or missing, those following it are not processed. Note that 623 # for optional arguments, we allow missing arguments and proceed because 624 # there may be other arguments following it. 625 failure_value = 'scoped_ptr<Params>()' 626 c.Append() 627 value_var = param.unix_name + '_value' 628 (c.Append('const base::Value* %(value_var)s = NULL;') 629 .Append('if (args.Get(%(i)s, &%(value_var)s) &&') 630 .Sblock(' !%(value_var)s->IsType(base::Value::TYPE_NULL)) {') 631 .Concat(self._GeneratePopulatePropertyFromValue( 632 param, value_var, 'params', failure_value)) 633 .Eblock('}') 634 ) 635 if not param.optional: 636 (c.Sblock('else {') 637 .Concat(self._GenerateError('"\'%%(key)s\' is required"')) 638 .Append('return %s;' % failure_value) 639 .Eblock('}')) 640 c.Substitute({'value_var': value_var, 'i': i, 'key': param.name}) 641 (c.Append() 642 .Append('return params.Pass();') 643 .Eblock('}') 644 .Append() 645 ) 646 647 return c 648 649 def _GeneratePopulatePropertyFromValue(self, 650 prop, 651 src_var, 652 dst_class_var, 653 failure_value): 654 """Generates code to populate property |prop| of |dst_class_var| (a 655 pointer) from a Value*. See |_GeneratePopulateVariableFromValue| for 656 semantics. 657 """ 658 return self._GeneratePopulateVariableFromValue(prop.type_, 659 src_var, 660 '%s->%s' % (dst_class_var, 661 prop.unix_name), 662 failure_value, 663 is_ptr=prop.optional) 664 665 def _GeneratePopulateVariableFromValue(self, 666 type_, 667 src_var, 668 dst_var, 669 failure_value, 670 is_ptr=False): 671 """Generates code to populate a variable |dst_var| of type |type_| from a 672 Value* at |src_var|. The Value* is assumed to be non-NULL. In the generated 673 code, if |dst_var| fails to be populated then Populate will return 674 |failure_value|. 675 """ 676 c = Code() 677 678 underlying_type = self._type_helper.FollowRef(type_) 679 680 if underlying_type.property_type.is_fundamental: 681 if is_ptr: 682 (c.Append('%(cpp_type)s temp;') 683 .Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue( 684 self._type_helper.FollowRef(type_), src_var, '&temp')) 685 .Concat(self._GenerateError( 686 '"\'%%(key)s\': expected ' + '%s, got " + %s' % ( 687 type_.name, 688 self._util_cc_helper.GetValueTypeString( 689 '%%(src_var)s', True))))) 690 c.Append('%(dst_var)s.reset();') 691 if not self._generate_error_messages: 692 c.Append('return %(failure_value)s;') 693 (c.Eblock('}') 694 .Append('else') 695 .Append(' %(dst_var)s.reset(new %(cpp_type)s(temp));') 696 ) 697 else: 698 (c.Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue( 699 self._type_helper.FollowRef(type_), 700 src_var, 701 '&%s' % dst_var)) 702 .Concat(self._GenerateError( 703 '"\'%%(key)s\': expected ' + '%s, got " + %s' % ( 704 type_.name, 705 self._util_cc_helper.GetValueTypeString( 706 '%%(src_var)s', True)))) 707 .Append('return %(failure_value)s;') 708 .Eblock('}') 709 ) 710 elif underlying_type.property_type == PropertyType.OBJECT: 711 if is_ptr: 712 (c.Append('const base::DictionaryValue* dictionary = NULL;') 713 .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') 714 .Concat(self._GenerateError( 715 '"\'%%(key)s\': expected dictionary, got " + ' + 716 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))) 717 # If an optional property fails to populate, the population can still 718 # succeed with a warning. If no error messages are generated, this 719 # warning is not set and we fail out instead. 720 if not self._generate_error_messages: 721 c.Append('return %(failure_value)s;') 722 (c.Eblock('}') 723 .Sblock('else {') 724 .Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') 725 .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( 726 ('*dictionary', 'temp.get()'))) 727 .Append(' return %(failure_value)s;') 728 ) 729 (c.Append('}') 730 .Append('else') 731 .Append(' %(dst_var)s = temp.Pass();') 732 .Eblock('}') 733 ) 734 else: 735 (c.Append('const base::DictionaryValue* dictionary = NULL;') 736 .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') 737 .Concat(self._GenerateError( 738 '"\'%%(key)s\': expected dictionary, got " + ' + 739 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 740 .Append('return %(failure_value)s;') 741 .Eblock('}') 742 .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( 743 ('*dictionary', '&%(dst_var)s'))) 744 .Append(' return %(failure_value)s;') 745 .Append('}') 746 ) 747 elif underlying_type.property_type == PropertyType.FUNCTION: 748 if is_ptr: 749 c.Append('%(dst_var)s.reset(new base::DictionaryValue());') 750 elif underlying_type.property_type == PropertyType.ANY: 751 c.Append('%(dst_var)s.reset(%(src_var)s->DeepCopy());') 752 elif underlying_type.property_type == PropertyType.ARRAY: 753 # util_cc_helper deals with optional and required arrays 754 (c.Append('const base::ListValue* list = NULL;') 755 .Sblock('if (!%(src_var)s->GetAsList(&list)) {') 756 .Concat(self._GenerateError( 757 '"\'%%(key)s\': expected list, got " + ' + 758 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 759 ) 760 if is_ptr and self._generate_error_messages: 761 c.Append('%(dst_var)s.reset();') 762 else: 763 c.Append('return %(failure_value)s;') 764 c.Eblock('}') 765 c.Sblock('else {') 766 item_type = self._type_helper.FollowRef(underlying_type.item_type) 767 if item_type.property_type == PropertyType.ENUM: 768 c.Concat(self._GenerateListValueToEnumArrayConversion( 769 item_type, 770 'list', 771 dst_var, 772 failure_value, 773 is_ptr=is_ptr)) 774 else: 775 (c.Sblock('if (!%s) {' % self._util_cc_helper.PopulateArrayFromList( 776 'list', 777 dst_var, 778 is_ptr))) 779 c.Concat(self._GenerateError( 780 '"unable to populate array \'%%(parent_key)s\'"')) 781 if is_ptr and self._generate_error_messages: 782 c.Append('%(dst_var)s.reset();') 783 else: 784 c.Append('return %(failure_value)s;') 785 c.Eblock('}') 786 c.Eblock('}') 787 elif underlying_type.property_type == PropertyType.CHOICES: 788 if is_ptr: 789 (c.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') 790 .Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( 791 ('*%(src_var)s', 'temp.get()'))) 792 .Append(' return %(failure_value)s;') 793 .Append('%(dst_var)s = temp.Pass();') 794 ) 795 else: 796 (c.Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( 797 ('*%(src_var)s', '&%(dst_var)s'))) 798 .Append(' return %(failure_value)s;')) 799 elif underlying_type.property_type == PropertyType.ENUM: 800 c.Concat(self._GenerateStringToEnumConversion(underlying_type, 801 src_var, 802 dst_var, 803 failure_value)) 804 elif underlying_type.property_type == PropertyType.BINARY: 805 (c.Append('const base::BinaryValue* binary_value = NULL;') 806 .Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {') 807 .Concat(self._GenerateError( 808 '"\'%%(key)s\': expected binary, got " + ' + 809 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 810 ) 811 if not self._generate_error_messages: 812 c.Append('return %(failure_value)s;') 813 (c.Eblock('}') 814 .Sblock('else {') 815 .Append(' binary_value =') 816 .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);') 817 ) 818 if is_ptr: 819 (c.Append('%(dst_var)s.reset(') 820 .Append(' new std::string(binary_value->GetBuffer(),') 821 .Append(' binary_value->GetSize()));') 822 ) 823 else: 824 (c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),') 825 .Append(' binary_value->GetSize());') 826 ) 827 c.Eblock('}') 828 else: 829 raise NotImplementedError(type_) 830 if c.IsEmpty(): 831 return c 832 return Code().Sblock('{').Concat(c.Substitute({ 833 'cpp_type': self._type_helper.GetCppType(type_), 834 'src_var': src_var, 835 'dst_var': dst_var, 836 'failure_value': failure_value, 837 'key': type_.name, 838 'parent_key': type_.parent.name, 839 })).Eblock('}') 840 841 def _GenerateListValueToEnumArrayConversion(self, 842 item_type, 843 src_var, 844 dst_var, 845 failure_value, 846 is_ptr=False): 847 """Returns Code that converts a ListValue of string constants from 848 |src_var| into an array of enums of |type_| in |dst_var|. On failure, 849 returns |failure_value|. 850 """ 851 c = Code() 852 accessor = '.' 853 if is_ptr: 854 accessor = '->' 855 cpp_type = self._type_helper.GetCppType(item_type, is_in_container=True) 856 c.Append('%s.reset(new std::vector<%s>);' % 857 (dst_var, cpp_util.PadForGenerics(cpp_type))) 858 (c.Sblock('for (base::ListValue::const_iterator it = %s->begin(); ' 859 'it != %s->end(); ++it) {' % (src_var, src_var)) 860 .Append('%s tmp;' % self._type_helper.GetCppType(item_type)) 861 .Concat(self._GenerateStringToEnumConversion(item_type, 862 '(*it)', 863 'tmp', 864 failure_value)) 865 .Append('%s%spush_back(tmp);' % (dst_var, accessor)) 866 .Eblock('}') 867 ) 868 return c 869 870 def _GenerateStringToEnumConversion(self, 871 type_, 872 src_var, 873 dst_var, 874 failure_value): 875 """Returns Code that converts a string type in |src_var| to an enum with 876 type |type_| in |dst_var|. In the generated code, if |src_var| is not 877 a valid enum name then the function will return |failure_value|. 878 """ 879 if type_.property_type != PropertyType.ENUM: 880 raise TypeError(type_) 881 c = Code() 882 enum_as_string = '%s_as_string' % type_.unix_name 883 cpp_type_namespace = '' 884 if type_.namespace != self._namespace: 885 cpp_type_namespace = '%s::' % type_.namespace.unix_name 886 cpp_type_name = self._type_helper.GetCppType(type_) 887 (c.Append('std::string %s;' % enum_as_string) 888 .Sblock('if (!%s->GetAsString(&%s)) {' % (src_var, enum_as_string)) 889 .Concat(self._GenerateError( 890 '"\'%%(key)s\': expected string, got " + ' + 891 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 892 .Append('return %s;' % failure_value) 893 .Eblock('}') 894 .Append('%s = %sParse%s(%s);' % (dst_var, 895 cpp_type_namespace, 896 cpp_util.Classname(type_.name), 897 enum_as_string)) 898 .Sblock('if (%s == %s%s) {' % (dst_var, 899 cpp_type_namespace, 900 self._type_helper.GetEnumNoneValue(type_))) 901 .Concat(self._GenerateError( 902 '\"\'%%(key)s\': expected \\"' + 903 '\\" or \\"'.join( 904 enum_value.name 905 for enum_value in self._type_helper.FollowRef(type_).enum_values) + 906 '\\", got \\"" + %s + "\\""' % enum_as_string)) 907 .Append('return %s;' % failure_value) 908 .Eblock('}') 909 .Substitute({'src_var': src_var, 'key': type_.name}) 910 ) 911 return c 912 913 def _GeneratePropertyFunctions(self, namespace, params): 914 """Generates the member functions for a list of parameters. 915 """ 916 return self._GenerateTypes(namespace, (param.type_ for param in params)) 917 918 def _GenerateTypes(self, namespace, types): 919 """Generates the member functions for a list of types. 920 """ 921 c = Code() 922 for type_ in types: 923 c.Cblock(self._GenerateType(namespace, type_)) 924 return c 925 926 def _GenerateEnumToString(self, cpp_namespace, type_): 927 """Generates ToString() which gets the string representation of an enum. 928 """ 929 c = Code() 930 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 931 932 if cpp_namespace is not None: 933 c.Append('// static') 934 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace 935 936 c.Sblock('std::string %sToString(%s enum_param) {' % 937 (maybe_namespace, classname)) 938 c.Sblock('switch (enum_param) {') 939 for enum_value in self._type_helper.FollowRef(type_).enum_values: 940 name = enum_value.name 941 if 'camel_case_enum_to_string' in self._namespace.compiler_options: 942 name = enum_value.CamelName() 943 (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value)) 944 .Append(' return "%s";' % name)) 945 (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) 946 .Append(' return "";') 947 .Eblock('}') 948 .Append('NOTREACHED();') 949 .Append('return "";') 950 .Eblock('}') 951 ) 952 return c 953 954 def _GenerateEnumFromString(self, cpp_namespace, type_): 955 """Generates FromClassNameString() which gets an enum from its string 956 representation. 957 """ 958 c = Code() 959 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 960 961 if cpp_namespace is not None: 962 c.Append('// static') 963 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace 964 965 c.Sblock('%s%s %sParse%s(const std::string& enum_string) {' % 966 (maybe_namespace, classname, maybe_namespace, classname)) 967 for i, enum_value in enumerate( 968 self._type_helper.FollowRef(type_).enum_values): 969 # This is broken up into all ifs with no else ifs because we get 970 # "fatal error C1061: compiler limit : blocks nested too deeply" 971 # on Windows. 972 (c.Append('if (enum_string == "%s")' % enum_value.name) 973 .Append(' return %s;' % 974 self._type_helper.GetEnumValue(type_, enum_value))) 975 (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) 976 .Eblock('}') 977 ) 978 return c 979 980 def _GenerateCreateCallbackArguments(self, 981 cpp_namespace, 982 function_scope, 983 callback): 984 """Generate all functions to create Value parameters for a callback. 985 986 E.g for function "Bar", generate Bar::Results::Create 987 E.g for event "Baz", generate Baz::Create 988 989 function_scope: the function scope path, e.g. Foo::Bar for the function 990 Foo::Bar::Baz(). May be None if there is no function scope. 991 callback: the Function object we are creating callback arguments for. 992 """ 993 c = Code() 994 params = callback.params 995 c.Concat(self._GeneratePropertyFunctions(function_scope, params)) 996 997 (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s' 998 'Create(%(declaration_list)s) {') 999 .Append('scoped_ptr<base::ListValue> create_results(' 1000 'new base::ListValue());') 1001 ) 1002 declaration_list = [] 1003 for param in params: 1004 declaration_list.append(cpp_util.GetParameterDeclaration( 1005 param, self._type_helper.GetCppType(param.type_))) 1006 c.Cblock(self._CreateValueFromType('create_results->Append(%s);', 1007 param.name, 1008 param.type_, 1009 param.unix_name)) 1010 c.Append('return create_results.Pass();') 1011 c.Eblock('}') 1012 c.Substitute({ 1013 'function_scope': ('%s::' % function_scope) if function_scope else '', 1014 'declaration_list': ', '.join(declaration_list), 1015 'param_names': ', '.join(param.unix_name for param in params) 1016 }) 1017 return c 1018 1019 def _GenerateEventNameConstant(self, function_scope, event): 1020 """Generates a constant string array for the event name. 1021 """ 1022 c = Code() 1023 c.Append('const char kEventName[] = "%s.%s";' % ( 1024 self._namespace.name, event.name)) 1025 return c 1026 1027 def _InitializePropertyToDefault(self, prop, dst): 1028 """Initialize a model.Property to its default value inside an object. 1029 1030 E.g for optional enum "state", generate dst->state = STATE_NONE; 1031 1032 dst: Type* 1033 """ 1034 c = Code() 1035 underlying_type = self._type_helper.FollowRef(prop.type_) 1036 if (underlying_type.property_type == PropertyType.ENUM and 1037 prop.optional): 1038 c.Append('%s->%s = %s;' % ( 1039 dst, 1040 prop.unix_name, 1041 self._type_helper.GetEnumNoneValue(prop.type_))) 1042 return c 1043 1044 def _GenerateError(self, body): 1045 """Generates an error message pertaining to population failure. 1046 1047 E.g 'expected bool, got int' 1048 """ 1049 c = Code() 1050 if not self._generate_error_messages: 1051 return c 1052 (c.Append('if (error->length())') 1053 .Append(' error->append(UTF8ToUTF16("; "));') 1054 .Append('error->append(UTF8ToUTF16(%s));' % body)) 1055 return c 1056 1057 def _GenerateParams(self, params): 1058 """Builds the parameter list for a function, given an array of parameters. 1059 """ 1060 if self._generate_error_messages: 1061 params = list(params) + ['base::string16* error'] 1062 return ', '.join(str(p) for p in params) 1063 1064 def _GenerateArgs(self, args): 1065 """Builds the argument list for a function, given an array of arguments. 1066 """ 1067 if self._generate_error_messages: 1068 args = list(args) + ['error'] 1069 return ', '.join(str(a) for a in args) 1070