cc_generator.py revision 010d83a9304c5a91596085d917d248abff47903a
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.Append('value->SetWithoutPathExpansion("%s", %s);' % ( 370 prop.name, 371 self._CreateValueFromType(cpp_namespace, 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 .Append('value->SetWithoutPathExpansion(it->first, %s);' % 394 self._CreateValueFromType( 395 cpp_namespace, 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 .Append('result.reset(%s);' % self._CreateValueFromType( 418 cpp_namespace, 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, cpp_namespace, 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 underlying_type = self._type_helper.FollowRef(type_) 486 if (underlying_type.property_type == PropertyType.CHOICES or 487 underlying_type.property_type == PropertyType.OBJECT): 488 if is_ptr: 489 return '(%s)->ToValue().release()' % var 490 else: 491 return '(%s).ToValue().release()' % var 492 elif (underlying_type.property_type == PropertyType.ANY or 493 underlying_type.property_type == PropertyType.FUNCTION): 494 if is_ptr: 495 vardot = '(%s)->' % var 496 else: 497 vardot = '(%s).' % var 498 return '%sDeepCopy()' % vardot 499 elif underlying_type.property_type == PropertyType.ENUM: 500 classname = cpp_util.Classname(schema_util.StripNamespace( 501 underlying_type.name)) 502 return 'new base::StringValue(%sToString(%s))' % (classname, var) 503 elif underlying_type.property_type == PropertyType.BINARY: 504 if is_ptr: 505 vardot = var + '->' 506 else: 507 vardot = var + '.' 508 return ('base::BinaryValue::CreateWithCopiedBuffer(%sdata(), %ssize())' % 509 (vardot, vardot)) 510 elif underlying_type.property_type == PropertyType.ARRAY: 511 return '%s.release()' % self._util_cc_helper.CreateValueFromArray( 512 cpp_namespace, 513 underlying_type, 514 var, 515 is_ptr) 516 elif underlying_type.property_type.is_fundamental: 517 if is_ptr: 518 var = '*%s' % var 519 if underlying_type.property_type == PropertyType.STRING: 520 return 'new base::StringValue(%s)' % var 521 else: 522 return 'new base::FundamentalValue(%s)' % var 523 else: 524 raise NotImplementedError('Conversion of %s to base::Value not ' 525 'implemented' % repr(type_.type_)) 526 527 def _GenerateParamsCheck(self, function, var): 528 """Generates a check for the correct number of arguments when creating 529 Params. 530 """ 531 c = Code() 532 num_required = 0 533 for param in function.params: 534 if not param.optional: 535 num_required += 1 536 if num_required == len(function.params): 537 c.Sblock('if (%(var)s.GetSize() != %(total)d) {') 538 elif not num_required: 539 c.Sblock('if (%(var)s.GetSize() > %(total)d) {') 540 else: 541 c.Sblock('if (%(var)s.GetSize() < %(required)d' 542 ' || %(var)s.GetSize() > %(total)d) {') 543 (c.Concat(self._GenerateError( 544 '"expected %%(total)d arguments, got " ' 545 '+ base::IntToString(%%(var)s.GetSize())')) 546 .Append('return scoped_ptr<Params>();') 547 .Eblock('}') 548 .Substitute({ 549 'var': var, 550 'required': num_required, 551 'total': len(function.params), 552 })) 553 return c 554 555 def _GenerateFunctionParamsCreate(self, function): 556 """Generate function to create an instance of Params. The generated 557 function takes a base::ListValue of arguments. 558 559 E.g for function "Bar", generate Bar::Params::Create() 560 """ 561 c = Code() 562 (c.Append('// static') 563 .Sblock('scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams( 564 ['const base::ListValue& args'])) 565 ) 566 if self._generate_error_messages: 567 c.Append('DCHECK(error);') 568 (c.Concat(self._GenerateParamsCheck(function, 'args')) 569 .Append('scoped_ptr<Params> params(new Params());') 570 ) 571 572 for param in function.params: 573 c.Concat(self._InitializePropertyToDefault(param, 'params')) 574 575 for i, param in enumerate(function.params): 576 # Any failure will cause this function to return. If any argument is 577 # incorrect or missing, those following it are not processed. Note that 578 # for optional arguments, we allow missing arguments and proceed because 579 # there may be other arguments following it. 580 failure_value = 'scoped_ptr<Params>()' 581 c.Append() 582 value_var = param.unix_name + '_value' 583 (c.Append('const base::Value* %(value_var)s = NULL;') 584 .Append('if (args.Get(%(i)s, &%(value_var)s) &&') 585 .Sblock(' !%(value_var)s->IsType(base::Value::TYPE_NULL)) {') 586 .Concat(self._GeneratePopulatePropertyFromValue( 587 param, value_var, 'params', failure_value)) 588 .Eblock('}') 589 ) 590 if not param.optional: 591 (c.Sblock('else {') 592 .Concat(self._GenerateError('"\'%%(key)s\' is required"')) 593 .Append('return %s;' % failure_value) 594 .Eblock('}')) 595 c.Substitute({'value_var': value_var, 'i': i, 'key': param.name}) 596 (c.Append() 597 .Append('return params.Pass();') 598 .Eblock('}') 599 .Append() 600 ) 601 602 return c 603 604 def _GeneratePopulatePropertyFromValue(self, 605 prop, 606 src_var, 607 dst_class_var, 608 failure_value): 609 """Generates code to populate property |prop| of |dst_class_var| (a 610 pointer) from a Value*. See |_GeneratePopulateVariableFromValue| for 611 semantics. 612 """ 613 return self._GeneratePopulateVariableFromValue(prop.type_, 614 src_var, 615 '%s->%s' % (dst_class_var, 616 prop.unix_name), 617 failure_value, 618 is_ptr=prop.optional) 619 620 def _GeneratePopulateVariableFromValue(self, 621 type_, 622 src_var, 623 dst_var, 624 failure_value, 625 is_ptr=False): 626 """Generates code to populate a variable |dst_var| of type |type_| from a 627 Value* at |src_var|. The Value* is assumed to be non-NULL. In the generated 628 code, if |dst_var| fails to be populated then Populate will return 629 |failure_value|. 630 """ 631 c = Code() 632 633 underlying_type = self._type_helper.FollowRef(type_) 634 635 if underlying_type.property_type.is_fundamental: 636 if is_ptr: 637 (c.Append('%(cpp_type)s temp;') 638 .Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue( 639 self._type_helper.FollowRef(type_), src_var, '&temp')) 640 .Concat(self._GenerateError( 641 '"\'%%(key)s\': expected ' + '%s, got " + %s' % ( 642 type_.name, 643 self._util_cc_helper.GetValueTypeString( 644 '%%(src_var)s', True))))) 645 c.Append('%(dst_var)s.reset();') 646 if not self._generate_error_messages: 647 c.Append('return %(failure_value)s;') 648 (c.Eblock('}') 649 .Append('else') 650 .Append(' %(dst_var)s.reset(new %(cpp_type)s(temp));') 651 ) 652 else: 653 (c.Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue( 654 self._type_helper.FollowRef(type_), 655 src_var, 656 '&%s' % dst_var)) 657 .Concat(self._GenerateError( 658 '"\'%%(key)s\': expected ' + '%s, got " + %s' % ( 659 type_.name, 660 self._util_cc_helper.GetValueTypeString( 661 '%%(src_var)s', True)))) 662 .Append('return %(failure_value)s;') 663 .Eblock('}') 664 ) 665 elif underlying_type.property_type == PropertyType.OBJECT: 666 if is_ptr: 667 (c.Append('const base::DictionaryValue* dictionary = NULL;') 668 .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') 669 .Concat(self._GenerateError( 670 '"\'%%(key)s\': expected dictionary, got " + ' + 671 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))) 672 # If an optional property fails to populate, the population can still 673 # succeed with a warning. If no error messages are generated, this 674 # warning is not set and we fail out instead. 675 if not self._generate_error_messages: 676 c.Append('return %(failure_value)s;') 677 (c.Eblock('}') 678 .Sblock('else {') 679 .Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') 680 .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( 681 ('*dictionary', 'temp.get()'))) 682 .Append(' return %(failure_value)s;') 683 ) 684 (c.Append('}') 685 .Append('else') 686 .Append(' %(dst_var)s = temp.Pass();') 687 .Eblock('}') 688 ) 689 else: 690 (c.Append('const base::DictionaryValue* dictionary = NULL;') 691 .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') 692 .Concat(self._GenerateError( 693 '"\'%%(key)s\': expected dictionary, got " + ' + 694 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 695 .Append('return %(failure_value)s;') 696 .Eblock('}') 697 .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( 698 ('*dictionary', '&%(dst_var)s'))) 699 .Append(' return %(failure_value)s;') 700 .Append('}') 701 ) 702 elif underlying_type.property_type == PropertyType.FUNCTION: 703 if is_ptr: 704 c.Append('%(dst_var)s.reset(new base::DictionaryValue());') 705 elif underlying_type.property_type == PropertyType.ANY: 706 c.Append('%(dst_var)s.reset(%(src_var)s->DeepCopy());') 707 elif underlying_type.property_type == PropertyType.ARRAY: 708 # util_cc_helper deals with optional and required arrays 709 (c.Append('const base::ListValue* list = NULL;') 710 .Sblock('if (!%(src_var)s->GetAsList(&list)) {') 711 .Concat(self._GenerateError( 712 '"\'%%(key)s\': expected list, got " + ' + 713 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 714 ) 715 if is_ptr and self._generate_error_messages: 716 c.Append('%(dst_var)s.reset();') 717 else: 718 c.Append('return %(failure_value)s;') 719 c.Eblock('}') 720 c.Sblock('else {') 721 item_type = self._type_helper.FollowRef(underlying_type.item_type) 722 if item_type.property_type == PropertyType.ENUM: 723 c.Concat(self._GenerateListValueToEnumArrayConversion( 724 item_type, 725 'list', 726 dst_var, 727 failure_value, 728 is_ptr=is_ptr)) 729 else: 730 (c.Sblock('if (!%s) {' % self._util_cc_helper.PopulateArrayFromList( 731 'list', 732 dst_var, 733 is_ptr))) 734 c.Concat(self._GenerateError( 735 '"unable to populate array \'%%(parent_key)s\'"')) 736 if is_ptr and self._generate_error_messages: 737 c.Append('%(dst_var)s.reset();') 738 else: 739 c.Append('return %(failure_value)s;') 740 c.Eblock('}') 741 c.Eblock('}') 742 elif underlying_type.property_type == PropertyType.CHOICES: 743 if is_ptr: 744 (c.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') 745 .Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( 746 ('*%(src_var)s', 'temp.get()'))) 747 .Append(' return %(failure_value)s;') 748 .Append('%(dst_var)s = temp.Pass();') 749 ) 750 else: 751 (c.Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( 752 ('*%(src_var)s', '&%(dst_var)s'))) 753 .Append(' return %(failure_value)s;')) 754 elif underlying_type.property_type == PropertyType.ENUM: 755 c.Concat(self._GenerateStringToEnumConversion(underlying_type, 756 src_var, 757 dst_var, 758 failure_value)) 759 elif underlying_type.property_type == PropertyType.BINARY: 760 (c.Append('const base::BinaryValue* binary_value = NULL;') 761 .Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {') 762 .Concat(self._GenerateError( 763 '"\'%%(key)s\': expected binary, got " + ' + 764 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 765 ) 766 if not self._generate_error_messages: 767 c.Append('return %(failure_value)s;') 768 (c.Eblock('}') 769 .Sblock('else {') 770 .Append(' binary_value =') 771 .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);') 772 ) 773 if is_ptr: 774 (c.Append('%(dst_var)s.reset(') 775 .Append(' new std::string(binary_value->GetBuffer(),') 776 .Append(' binary_value->GetSize()));') 777 ) 778 else: 779 (c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),') 780 .Append(' binary_value->GetSize());') 781 ) 782 c.Eblock('}') 783 else: 784 raise NotImplementedError(type_) 785 if c.IsEmpty(): 786 return c 787 return Code().Sblock('{').Concat(c.Substitute({ 788 'cpp_type': self._type_helper.GetCppType(type_), 789 'src_var': src_var, 790 'dst_var': dst_var, 791 'failure_value': failure_value, 792 'key': type_.name, 793 'parent_key': type_.parent.name, 794 })).Eblock('}') 795 796 def _GenerateListValueToEnumArrayConversion(self, 797 item_type, 798 src_var, 799 dst_var, 800 failure_value, 801 is_ptr=False): 802 """Returns Code that converts a ListValue of string constants from 803 |src_var| into an array of enums of |type_| in |dst_var|. On failure, 804 returns |failure_value|. 805 """ 806 c = Code() 807 accessor = '.' 808 if is_ptr: 809 accessor = '->' 810 cpp_type = self._type_helper.GetCppType(item_type, is_in_container=True) 811 c.Append('%s.reset(new std::vector<%s>);' % 812 (dst_var, cpp_util.PadForGenerics(cpp_type))) 813 (c.Sblock('for (base::ListValue::const_iterator it = %s->begin(); ' 814 'it != %s->end(); ++it) {' % (src_var, src_var)) 815 .Append('%s tmp;' % self._type_helper.GetCppType(item_type)) 816 .Concat(self._GenerateStringToEnumConversion(item_type, 817 '(*it)', 818 'tmp', 819 failure_value)) 820 .Append('%s%spush_back(tmp);' % (dst_var, accessor)) 821 .Eblock('}') 822 ) 823 return c 824 825 def _GenerateStringToEnumConversion(self, 826 type_, 827 src_var, 828 dst_var, 829 failure_value): 830 """Returns Code that converts a string type in |src_var| to an enum with 831 type |type_| in |dst_var|. In the generated code, if |src_var| is not 832 a valid enum name then the function will return |failure_value|. 833 """ 834 if type_.property_type != PropertyType.ENUM: 835 raise TypeError(type_) 836 c = Code() 837 enum_as_string = '%s_as_string' % type_.unix_name 838 cpp_type_namespace = '' 839 if type_.namespace != self._namespace: 840 cpp_type_namespace = '%s::' % type_.namespace.unix_name 841 cpp_type_name = self._type_helper.GetCppType(type_) 842 (c.Append('std::string %s;' % enum_as_string) 843 .Sblock('if (!%s->GetAsString(&%s)) {' % (src_var, enum_as_string)) 844 .Concat(self._GenerateError( 845 '"\'%%(key)s\': expected string, got " + ' + 846 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 847 .Append('return %s;' % failure_value) 848 .Eblock('}') 849 .Append('%s = %sParse%s(%s);' % (dst_var, 850 cpp_type_namespace, 851 cpp_util.Classname(type_.name), 852 enum_as_string)) 853 .Sblock('if (%s == %s%s) {' % (dst_var, 854 cpp_type_namespace, 855 self._type_helper.GetEnumNoneValue(type_))) 856 .Concat(self._GenerateError( 857 '\"\'%%(key)s\': expected \\"' + 858 '\\" or \\"'.join( 859 enum_value.name 860 for enum_value in self._type_helper.FollowRef(type_).enum_values) + 861 '\\", got \\"" + %s + "\\""' % enum_as_string)) 862 .Append('return %s;' % failure_value) 863 .Eblock('}') 864 .Substitute({'src_var': src_var, 'key': type_.name}) 865 ) 866 return c 867 868 def _GeneratePropertyFunctions(self, namespace, params): 869 """Generates the member functions for a list of parameters. 870 """ 871 return self._GenerateTypes(namespace, (param.type_ for param in params)) 872 873 def _GenerateTypes(self, namespace, types): 874 """Generates the member functions for a list of types. 875 """ 876 c = Code() 877 for type_ in types: 878 c.Cblock(self._GenerateType(namespace, type_)) 879 return c 880 881 def _GenerateEnumToString(self, cpp_namespace, type_): 882 """Generates ToString() which gets the string representation of an enum. 883 """ 884 c = Code() 885 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 886 887 if cpp_namespace is not None: 888 c.Append('// static') 889 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace 890 891 c.Sblock('std::string %s%sToString(%s enum_param) {' % 892 (maybe_namespace, classname, classname)) 893 c.Sblock('switch (enum_param) {') 894 for enum_value in self._type_helper.FollowRef(type_).enum_values: 895 (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value)) 896 .Append(' return "%s";' % enum_value.name)) 897 (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) 898 .Append(' return "";') 899 .Eblock('}') 900 .Append('NOTREACHED();') 901 .Append('return "";') 902 .Eblock('}') 903 ) 904 905 (c.Append() 906 .Sblock('std::string %sToString(%s enum_param) {' % 907 (maybe_namespace, classname)) 908 .Append('return %s%sToString(enum_param);' % (maybe_namespace, classname)) 909 .Eblock('}')) 910 return c 911 912 def _GenerateEnumFromString(self, cpp_namespace, type_): 913 """Generates FromClassNameString() which gets an enum from its string 914 representation. 915 """ 916 c = Code() 917 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 918 919 if cpp_namespace is not None: 920 c.Append('// static') 921 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace 922 923 c.Sblock('%s%s %sParse%s(const std::string& enum_string) {' % 924 (maybe_namespace, classname, maybe_namespace, classname)) 925 for i, enum_value in enumerate( 926 self._type_helper.FollowRef(type_).enum_values): 927 # This is broken up into all ifs with no else ifs because we get 928 # "fatal error C1061: compiler limit : blocks nested too deeply" 929 # on Windows. 930 (c.Append('if (enum_string == "%s")' % enum_value.name) 931 .Append(' return %s;' % 932 self._type_helper.GetEnumValue(type_, enum_value))) 933 (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) 934 .Eblock('}') 935 ) 936 return c 937 938 def _GenerateCreateCallbackArguments(self, 939 cpp_namespace, 940 function_scope, 941 callback): 942 """Generate all functions to create Value parameters for a callback. 943 944 E.g for function "Bar", generate Bar::Results::Create 945 E.g for event "Baz", generate Baz::Create 946 947 function_scope: the function scope path, e.g. Foo::Bar for the function 948 Foo::Bar::Baz(). May be None if there is no function scope. 949 callback: the Function object we are creating callback arguments for. 950 """ 951 c = Code() 952 params = callback.params 953 c.Concat(self._GeneratePropertyFunctions(function_scope, params)) 954 955 (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s' 956 'Create(%(declaration_list)s) {') 957 .Append('scoped_ptr<base::ListValue> create_results(' 958 'new base::ListValue());') 959 ) 960 declaration_list = [] 961 for param in params: 962 declaration_list.append(cpp_util.GetParameterDeclaration( 963 param, self._type_helper.GetCppType(param.type_))) 964 c.Append('create_results->Append(%s);' % self._CreateValueFromType( 965 cpp_namespace, 966 param.type_, 967 param.unix_name)) 968 c.Append('return create_results.Pass();') 969 c.Eblock('}') 970 c.Substitute({ 971 'function_scope': ('%s::' % function_scope) if function_scope else '', 972 'declaration_list': ', '.join(declaration_list), 973 'param_names': ', '.join(param.unix_name for param in params) 974 }) 975 return c 976 977 def _GenerateEventNameConstant(self, function_scope, event): 978 """Generates a constant string array for the event name. 979 """ 980 c = Code() 981 c.Append('const char kEventName[] = "%s.%s";' % ( 982 self._namespace.name, event.name)) 983 return c 984 985 def _InitializePropertyToDefault(self, prop, dst): 986 """Initialize a model.Property to its default value inside an object. 987 988 E.g for optional enum "state", generate dst->state = STATE_NONE; 989 990 dst: Type* 991 """ 992 c = Code() 993 underlying_type = self._type_helper.FollowRef(prop.type_) 994 if (underlying_type.property_type == PropertyType.ENUM and 995 prop.optional): 996 c.Append('%s->%s = %s;' % ( 997 dst, 998 prop.unix_name, 999 self._type_helper.GetEnumNoneValue(prop.type_))) 1000 return c 1001 1002 def _GenerateError(self, body): 1003 """Generates an error message pertaining to population failure. 1004 1005 E.g 'expected bool, got int' 1006 """ 1007 c = Code() 1008 if not self._generate_error_messages: 1009 return c 1010 (c.Append('if (error->length())') 1011 .Append(' error->append(UTF8ToUTF16("; "));') 1012 .Append('error->append(UTF8ToUTF16(%s));' % body)) 1013 return c 1014 1015 def _GenerateParams(self, params): 1016 """Builds the parameter list for a function, given an array of parameters. 1017 """ 1018 if self._generate_error_messages: 1019 params = list(params) + ['base::string16* error'] 1020 return ', '.join(str(p) for p in params) 1021 1022 def _GenerateArgs(self, args): 1023 """Builds the argument list for a function, given an array of arguments. 1024 """ 1025 if self._generate_error_messages: 1026 args = list(args) + ['error'] 1027 return ', '.join(str(a) for a in args) 1028