cc_generator.py revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 type_.property_type == PropertyType.CHOICES: 195 for choice in type_.choices: 196 (c.Sblock('if (%s) {' % self._GenerateValueIsTypeExpression('value', 197 choice)) 198 .Concat(self._GeneratePopulateVariableFromValue( 199 choice, 200 '(&value)', 201 'out->as_%s' % choice.unix_name, 202 'false', 203 is_ptr=True)) 204 .Append('return true;') 205 .Eblock('}') 206 ) 207 (c.Concat(self._GenerateError( 208 '"expected %s, got " + %s' % 209 (" or ".join(choice.name for choice in type_.choices), 210 self._util_cc_helper.GetValueTypeString('value')))) 211 .Append('return false;')) 212 elif type_.property_type == PropertyType.OBJECT: 213 (c.Sblock('if (!value.IsType(base::Value::TYPE_DICTIONARY)) {') 214 .Concat(self._GenerateError( 215 '"expected dictionary, got " + ' + 216 self._util_cc_helper.GetValueTypeString('value'))) 217 .Append('return false;') 218 .Eblock('}')) 219 220 if type_.properties or type_.additional_properties is not None: 221 c.Append('const base::DictionaryValue* dict = ' 222 'static_cast<const base::DictionaryValue*>(&value);') 223 if self._generate_error_messages: 224 c.Append('std::set<std::string> keys;') 225 for prop in type_.properties.itervalues(): 226 c.Concat(self._InitializePropertyToDefault(prop, 'out')) 227 for prop in type_.properties.itervalues(): 228 if self._generate_error_messages: 229 c.Append('keys.insert("%s");' % (prop.name)) 230 c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) 231 # Check for extra values. 232 if self._generate_error_messages: 233 (c.Sblock('for (base::DictionaryValue::Iterator it(*dict); ' 234 '!it.IsAtEnd(); it.Advance()) {') 235 .Sblock('if (!keys.count(it.key())) {') 236 .Concat(self._GenerateError('"found unexpected key \'" + ' 237 'it.key() + "\'"')) 238 .Eblock('}') 239 .Eblock('}') 240 ) 241 if type_.additional_properties is not None: 242 if type_.additional_properties.property_type == PropertyType.ANY: 243 c.Append('out->additional_properties.MergeDictionary(dict);') 244 else: 245 cpp_type = self._type_helper.GetCppType(type_.additional_properties, 246 is_in_container=True) 247 (c.Append('for (base::DictionaryValue::Iterator it(*dict);') 248 .Sblock(' !it.IsAtEnd(); it.Advance()) {') 249 .Append('%s tmp;' % cpp_type) 250 .Concat(self._GeneratePopulateVariableFromValue( 251 type_.additional_properties, 252 '(&it.value())', 253 'tmp', 254 'false')) 255 .Append('out->additional_properties[it.key()] = tmp;') 256 .Eblock('}') 257 ) 258 c.Append('return true;') 259 (c.Eblock('}') 260 .Substitute({'namespace': cpp_namespace, 'name': classname})) 261 return c 262 263 def _GenerateValueIsTypeExpression(self, var, type_): 264 real_type = self._type_helper.FollowRef(type_) 265 if real_type.property_type is PropertyType.CHOICES: 266 return '(%s)' % ' || '.join(self._GenerateValueIsTypeExpression(var, 267 choice) 268 for choice in real_type.choices) 269 return '%s.IsType(%s)' % (var, cpp_util.GetValueType(real_type)) 270 271 def _GenerateTypePopulateProperty(self, prop, src, dst): 272 """Generate the code to populate a single property in a type. 273 274 src: base::DictionaryValue* 275 dst: Type* 276 """ 277 c = Code() 278 value_var = prop.unix_name + '_value' 279 c.Append('const base::Value* %(value_var)s = NULL;') 280 if prop.optional: 281 (c.Sblock( 282 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {') 283 .Concat(self._GeneratePopulatePropertyFromValue( 284 prop, value_var, dst, 'false'))) 285 underlying_type = self._type_helper.FollowRef(prop.type_) 286 if underlying_type.property_type == PropertyType.ENUM: 287 (c.Append('} else {') 288 .Append('%%(dst)s->%%(name)s = %s;' % 289 self._type_helper.GetEnumNoneValue(prop.type_))) 290 c.Eblock('}') 291 else: 292 (c.Sblock( 293 'if (!%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {') 294 .Concat(self._GenerateError('"\'%%(key)s\' is required"')) 295 .Append('return false;') 296 .Eblock('}') 297 .Concat(self._GeneratePopulatePropertyFromValue( 298 prop, value_var, dst, 'false')) 299 ) 300 c.Append() 301 c.Substitute({ 302 'value_var': value_var, 303 'key': prop.name, 304 'src': src, 305 'dst': dst, 306 'name': prop.unix_name 307 }) 308 return c 309 310 def _GenerateTypeFromValue(self, cpp_namespace, type_): 311 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 312 c = Code() 313 (c.Append('// static') 314 .Append('scoped_ptr<%s> %s::FromValue(%s) {' % (classname, 315 cpp_namespace, self._GenerateParams(('const base::Value& value',)))) 316 .Append(' scoped_ptr<%s> out(new %s());' % (classname, classname)) 317 .Append(' if (!Populate(%s))' % self._GenerateArgs( 318 ('value', 'out.get()'))) 319 .Append(' return scoped_ptr<%s>();' % classname) 320 .Append(' return out.Pass();') 321 .Append('}') 322 ) 323 return c 324 325 def _GenerateTypeToValue(self, cpp_namespace, type_): 326 """Generates a function that serializes the type into a base::Value. 327 E.g. for type "Foo" generates Foo::ToValue() 328 """ 329 if type_.property_type == PropertyType.OBJECT: 330 return self._GenerateObjectTypeToValue(cpp_namespace, type_) 331 elif type_.property_type == PropertyType.CHOICES: 332 return self._GenerateChoiceTypeToValue(cpp_namespace, type_) 333 else: 334 raise ValueError("Unsupported property type %s" % type_.type_) 335 336 def _GenerateObjectTypeToValue(self, cpp_namespace, type_): 337 """Generates a function that serializes an object-representing type 338 into a base::DictionaryValue. 339 """ 340 c = Code() 341 (c.Sblock('scoped_ptr<base::DictionaryValue> %s::ToValue() const {' % 342 cpp_namespace) 343 .Append('scoped_ptr<base::DictionaryValue> value(' 344 'new base::DictionaryValue());') 345 .Append() 346 ) 347 348 for prop in type_.properties.values(): 349 prop_var = 'this->%s' % prop.unix_name 350 if prop.optional: 351 # Optional enum values are generated with a NONE enum value. 352 underlying_type = self._type_helper.FollowRef(prop.type_) 353 if underlying_type.property_type == PropertyType.ENUM: 354 c.Sblock('if (%s != %s) {' % 355 (prop_var, 356 self._type_helper.GetEnumNoneValue(prop.type_))) 357 else: 358 c.Sblock('if (%s.get()) {' % prop_var) 359 360 # ANY is a base::Value which is abstract and cannot be a direct member, so 361 # it will always be a pointer. 362 is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY 363 c.Append('value->SetWithoutPathExpansion("%s", %s);' % ( 364 prop.name, 365 self._CreateValueFromType(prop.type_, prop_var, is_ptr=is_ptr))) 366 367 if prop.optional: 368 c.Eblock('}') 369 370 if type_.additional_properties is not None: 371 if type_.additional_properties.property_type == PropertyType.ANY: 372 c.Append('value->MergeDictionary(&additional_properties);') 373 else: 374 # Non-copyable types will be wrapped in a linked_ptr for inclusion in 375 # maps, so we need to unwrap them. 376 needs_unwrap = ( 377 not self._type_helper.IsCopyable(type_.additional_properties)) 378 cpp_type = self._type_helper.GetCppType(type_.additional_properties, 379 is_in_container=True) 380 (c.Sblock('for (std::map<std::string, %s>::const_iterator it =' % 381 cpp_util.PadForGenerics(cpp_type)) 382 .Append(' additional_properties.begin();') 383 .Append(' it != additional_properties.end(); ++it) {') 384 .Append('value->SetWithoutPathExpansion(it->first, %s);' % 385 self._CreateValueFromType( 386 type_.additional_properties, 387 '%sit->second' % ('*' if needs_unwrap else ''))) 388 .Eblock('}') 389 ) 390 391 return (c.Append() 392 .Append('return value.Pass();') 393 .Eblock('}')) 394 395 def _GenerateChoiceTypeToValue(self, cpp_namespace, type_): 396 """Generates a function that serializes a choice-representing type 397 into a base::Value. 398 """ 399 c = Code() 400 c.Sblock('scoped_ptr<base::Value> %s::ToValue() const {' % cpp_namespace) 401 c.Append('scoped_ptr<base::Value> result;') 402 for choice in type_.choices: 403 choice_var = 'as_%s' % choice.unix_name 404 (c.Sblock('if (%s) {' % choice_var) 405 .Append('DCHECK(!result) << "Cannot set multiple choices for %s";' % 406 type_.unix_name) 407 .Append('result.reset(%s);' % 408 self._CreateValueFromType(choice, '*%s' % choice_var)) 409 .Eblock('}') 410 ) 411 (c.Append('DCHECK(result) << "Must set at least one choice for %s";' % 412 type_.unix_name) 413 .Append('return result.Pass();') 414 .Eblock('}') 415 ) 416 return c 417 418 def _GenerateFunction(self, function): 419 """Generates the definitions for function structs. 420 """ 421 c = Code() 422 423 # TODO(kalman): use function.unix_name not Classname. 424 function_namespace = cpp_util.Classname(function.name) 425 # Windows has a #define for SendMessage, so to avoid any issues, we need 426 # to not use the name. 427 if function_namespace == 'SendMessage': 428 function_namespace = 'PassMessage' 429 (c.Append('namespace %s {' % function_namespace) 430 .Append() 431 ) 432 433 # Params::Populate function 434 if function.params: 435 c.Concat(self._GeneratePropertyFunctions('Params', function.params)) 436 (c.Append('Params::Params() {}') 437 .Append('Params::~Params() {}') 438 .Append() 439 .Cblock(self._GenerateFunctionParamsCreate(function)) 440 ) 441 442 # Results::Create function 443 if function.callback: 444 c.Concat(self._GenerateCreateCallbackArguments('Results', 445 function.callback)) 446 447 c.Append('} // namespace %s' % function_namespace) 448 return c 449 450 def _GenerateEvent(self, event): 451 # TODO(kalman): use event.unix_name not Classname. 452 c = Code() 453 event_namespace = cpp_util.Classname(event.name) 454 (c.Append('namespace %s {' % event_namespace) 455 .Append() 456 .Cblock(self._GenerateEventNameConstant(None, event)) 457 .Cblock(self._GenerateCreateCallbackArguments(None, event)) 458 .Append('} // namespace %s' % event_namespace) 459 ) 460 return c 461 462 def _CreateValueFromType(self, type_, var, is_ptr=False): 463 """Creates a base::Value given a type. Generated code passes ownership 464 to caller. 465 466 var: variable or variable* 467 468 E.g for std::string, generate new base::StringValue(var) 469 """ 470 underlying_type = self._type_helper.FollowRef(type_) 471 if (underlying_type.property_type == PropertyType.CHOICES or 472 underlying_type.property_type == PropertyType.OBJECT): 473 if is_ptr: 474 return '(%s)->ToValue().release()' % var 475 else: 476 return '(%s).ToValue().release()' % var 477 elif (underlying_type.property_type == PropertyType.ANY or 478 underlying_type.property_type == PropertyType.FUNCTION): 479 if is_ptr: 480 vardot = '(%s)->' % var 481 else: 482 vardot = '(%s).' % var 483 return '%sDeepCopy()' % vardot 484 elif underlying_type.property_type == PropertyType.ENUM: 485 return 'new base::StringValue(ToString(%s))' % var 486 elif underlying_type.property_type == PropertyType.BINARY: 487 if is_ptr: 488 vardot = var + '->' 489 else: 490 vardot = var + '.' 491 return ('base::BinaryValue::CreateWithCopiedBuffer(%sdata(), %ssize())' % 492 (vardot, vardot)) 493 elif underlying_type.property_type == PropertyType.ARRAY: 494 return '%s.release()' % self._util_cc_helper.CreateValueFromArray( 495 underlying_type, 496 var, 497 is_ptr) 498 elif underlying_type.property_type.is_fundamental: 499 if is_ptr: 500 var = '*%s' % var 501 if underlying_type.property_type == PropertyType.STRING: 502 return 'new base::StringValue(%s)' % var 503 else: 504 return 'new base::FundamentalValue(%s)' % var 505 else: 506 raise NotImplementedError('Conversion of %s to base::Value not ' 507 'implemented' % repr(type_.type_)) 508 509 def _GenerateParamsCheck(self, function, var): 510 """Generates a check for the correct number of arguments when creating 511 Params. 512 """ 513 c = Code() 514 num_required = 0 515 for param in function.params: 516 if not param.optional: 517 num_required += 1 518 if num_required == len(function.params): 519 c.Sblock('if (%(var)s.GetSize() != %(total)d) {') 520 elif not num_required: 521 c.Sblock('if (%(var)s.GetSize() > %(total)d) {') 522 else: 523 c.Sblock('if (%(var)s.GetSize() < %(required)d' 524 ' || %(var)s.GetSize() > %(total)d) {') 525 (c.Concat(self._GenerateError( 526 '"expected %%(total)d arguments, got " ' 527 '+ base::IntToString(%%(var)s.GetSize())')) 528 .Append('return scoped_ptr<Params>();') 529 .Eblock('}') 530 .Substitute({ 531 'var': var, 532 'required': num_required, 533 'total': len(function.params), 534 })) 535 return c 536 537 def _GenerateFunctionParamsCreate(self, function): 538 """Generate function to create an instance of Params. The generated 539 function takes a base::ListValue of arguments. 540 541 E.g for function "Bar", generate Bar::Params::Create() 542 """ 543 c = Code() 544 (c.Append('// static') 545 .Sblock('scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams( 546 ['const base::ListValue& args'])) 547 .Concat(self._GenerateParamsCheck(function, 'args')) 548 .Append('scoped_ptr<Params> params(new Params());') 549 ) 550 551 for param in function.params: 552 c.Concat(self._InitializePropertyToDefault(param, 'params')) 553 554 for i, param in enumerate(function.params): 555 # Any failure will cause this function to return. If any argument is 556 # incorrect or missing, those following it are not processed. Note that 557 # for optional arguments, we allow missing arguments and proceed because 558 # there may be other arguments following it. 559 failure_value = 'scoped_ptr<Params>()' 560 c.Append() 561 value_var = param.unix_name + '_value' 562 (c.Append('const base::Value* %(value_var)s = NULL;') 563 .Append('if (args.Get(%(i)s, &%(value_var)s) &&') 564 .Sblock(' !%(value_var)s->IsType(base::Value::TYPE_NULL)) {') 565 .Concat(self._GeneratePopulatePropertyFromValue( 566 param, value_var, 'params', failure_value)) 567 .Eblock('}') 568 ) 569 if not param.optional: 570 (c.Sblock('else {') 571 .Concat(self._GenerateError('"\'%%(key)s\' is required"')) 572 .Append('return %s;' % failure_value) 573 .Eblock('}')) 574 c.Substitute({'value_var': value_var, 'i': i, 'key': param.name}) 575 (c.Append() 576 .Append('return params.Pass();') 577 .Eblock('}') 578 .Append() 579 ) 580 581 return c 582 583 def _GeneratePopulatePropertyFromValue(self, 584 prop, 585 src_var, 586 dst_class_var, 587 failure_value): 588 """Generates code to populate property |prop| of |dst_class_var| (a 589 pointer) from a Value*. See |_GeneratePopulateVariableFromValue| for 590 semantics. 591 """ 592 return self._GeneratePopulateVariableFromValue(prop.type_, 593 src_var, 594 '%s->%s' % (dst_class_var, 595 prop.unix_name), 596 failure_value, 597 is_ptr=prop.optional) 598 599 def _GeneratePopulateVariableFromValue(self, 600 type_, 601 src_var, 602 dst_var, 603 failure_value, 604 is_ptr=False): 605 """Generates code to populate a variable |dst_var| of type |type_| from a 606 Value* at |src_var|. The Value* is assumed to be non-NULL. In the generated 607 code, if |dst_var| fails to be populated then Populate will return 608 |failure_value|. 609 """ 610 c = Code() 611 612 underlying_type = self._type_helper.FollowRef(type_) 613 614 if underlying_type.property_type.is_fundamental: 615 if is_ptr: 616 (c.Append('%(cpp_type)s temp;') 617 .Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue( 618 self._type_helper.FollowRef(type_), src_var, '&temp')) 619 .Concat(self._GenerateError( 620 '"\'%%(key)s\': expected ' + '%s, got " + %s' % ( 621 type_.name, 622 self._util_cc_helper.GetValueTypeString( 623 '%%(src_var)s', True))))) 624 c.Append('%(dst_var)s.reset();') 625 if not self._generate_error_messages: 626 c.Append('return %(failure_value)s;') 627 (c.Eblock('}') 628 .Append('else') 629 .Append(' %(dst_var)s.reset(new %(cpp_type)s(temp));') 630 ) 631 else: 632 (c.Sblock('if (!%s) {' % cpp_util.GetAsFundamentalValue( 633 self._type_helper.FollowRef(type_), 634 src_var, 635 '&%s' % dst_var)) 636 .Concat(self._GenerateError( 637 '"\'%%(key)s\': expected ' + '%s, got " + %s' % ( 638 type_.name, 639 self._util_cc_helper.GetValueTypeString( 640 '%%(src_var)s', True)))) 641 .Append('return %(failure_value)s;') 642 .Eblock('}') 643 ) 644 elif underlying_type.property_type == PropertyType.OBJECT: 645 if is_ptr: 646 (c.Append('const base::DictionaryValue* dictionary = NULL;') 647 .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') 648 .Concat(self._GenerateError( 649 '"\'%%(key)s\': expected dictionary, got " + ' + 650 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))) 651 # If an optional property fails to populate, the population can still 652 # succeed with a warning. If no error messages are generated, this 653 # warning is not set and we fail out instead. 654 if not self._generate_error_messages: 655 c.Append('return %(failure_value)s;') 656 (c.Eblock('}') 657 .Sblock('else {') 658 .Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') 659 .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( 660 ('*dictionary', 'temp.get()'))) 661 .Append(' return %(failure_value)s;') 662 ) 663 (c.Append('}') 664 .Append('else') 665 .Append(' %(dst_var)s = temp.Pass();') 666 .Eblock('}') 667 ) 668 else: 669 (c.Append('const base::DictionaryValue* dictionary = NULL;') 670 .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') 671 .Concat(self._GenerateError( 672 '"\'%%(key)s\': expected dictionary, got " + ' + 673 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 674 .Append('return %(failure_value)s;') 675 .Eblock('}') 676 .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( 677 ('*dictionary', '&%(dst_var)s'))) 678 .Append(' return %(failure_value)s;') 679 .Append('}') 680 ) 681 elif underlying_type.property_type == PropertyType.FUNCTION: 682 if is_ptr: 683 c.Append('%(dst_var)s.reset(new base::DictionaryValue());') 684 elif underlying_type.property_type == PropertyType.ANY: 685 c.Append('%(dst_var)s.reset(%(src_var)s->DeepCopy());') 686 elif underlying_type.property_type == PropertyType.ARRAY: 687 # util_cc_helper deals with optional and required arrays 688 (c.Append('const base::ListValue* list = NULL;') 689 .Sblock('if (!%(src_var)s->GetAsList(&list)) {') 690 .Concat(self._GenerateError( 691 '"\'%%(key)s\': expected list, got " + ' + 692 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 693 ) 694 if is_ptr and self._generate_error_messages: 695 c.Append('%(dst_var)s.reset();') 696 else: 697 c.Append('return %(failure_value)s;') 698 c.Eblock('}') 699 c.Sblock('else {') 700 item_type = self._type_helper.FollowRef(underlying_type.item_type) 701 if item_type.property_type == PropertyType.ENUM: 702 c.Concat(self._GenerateListValueToEnumArrayConversion( 703 item_type, 704 'list', 705 dst_var, 706 failure_value, 707 is_ptr=is_ptr)) 708 else: 709 (c.Sblock('if (!%s) {' % self._util_cc_helper.PopulateArrayFromList( 710 underlying_type, 711 'list', 712 dst_var, 713 is_ptr))) 714 c.Concat(self._GenerateError( 715 '"unable to populate array \'%%(parent_key)s\'"')) 716 if is_ptr and self._generate_error_messages: 717 c.Append('%(dst_var)s.reset();') 718 else: 719 c.Append('return %(failure_value)s;') 720 c.Eblock('}') 721 c.Eblock('}') 722 elif underlying_type.property_type == PropertyType.CHOICES: 723 if is_ptr: 724 (c.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') 725 .Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( 726 ('*%(src_var)s', 'temp.get()'))) 727 .Append(' return %(failure_value)s;') 728 .Append('%(dst_var)s = temp.Pass();') 729 ) 730 else: 731 (c.Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( 732 ('*%(src_var)s', '&%(dst_var)s'))) 733 .Append(' return %(failure_value)s;')) 734 elif underlying_type.property_type == PropertyType.ENUM: 735 c.Concat(self._GenerateStringToEnumConversion(underlying_type, 736 src_var, 737 dst_var, 738 failure_value)) 739 elif underlying_type.property_type == PropertyType.BINARY: 740 (c.Append('const base::BinaryValue* binary_value = NULL;') 741 .Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {') 742 .Concat(self._GenerateError( 743 '"\'%%(key)s\': expected binary, got " + ' + 744 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 745 ) 746 if not self._generate_error_messages: 747 c.Append('return %(failure_value)s;') 748 (c.Eblock('}') 749 .Sblock('else {') 750 .Append(' binary_value =') 751 .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);') 752 ) 753 if is_ptr: 754 (c.Append('%(dst_var)s.reset(') 755 .Append(' new std::string(binary_value->GetBuffer(),') 756 .Append(' binary_value->GetSize()));') 757 ) 758 else: 759 (c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),') 760 .Append(' binary_value->GetSize());') 761 ) 762 c.Eblock('}') 763 else: 764 raise NotImplementedError(type_) 765 if c.IsEmpty(): 766 return c 767 return Code().Sblock('{').Concat(c.Substitute({ 768 'cpp_type': self._type_helper.GetCppType(type_), 769 'src_var': src_var, 770 'dst_var': dst_var, 771 'failure_value': failure_value, 772 'key': type_.name, 773 'parent_key': type_.parent.name, 774 })).Eblock('}') 775 776 def _GenerateListValueToEnumArrayConversion(self, 777 item_type, 778 src_var, 779 dst_var, 780 failure_value, 781 is_ptr=False): 782 """Returns Code that converts a ListValue of string constants from 783 |src_var| into an array of enums of |type_| in |dst_var|. On failure, 784 returns |failure_value|. 785 """ 786 c = Code() 787 accessor = '.' 788 if is_ptr: 789 accessor = '->' 790 cpp_type = self._type_helper.GetCppType(item_type, is_in_container=True) 791 c.Append('%s.reset(new std::vector<%s>);' % 792 (dst_var, cpp_util.PadForGenerics(cpp_type))) 793 (c.Sblock('for (base::ListValue::const_iterator it = %s->begin(); ' 794 'it != %s->end(); ++it) {' % (src_var, src_var)) 795 .Append('%s tmp;' % self._type_helper.GetCppType(item_type)) 796 .Concat(self._GenerateStringToEnumConversion(item_type, 797 '(*it)', 798 'tmp', 799 failure_value)) 800 .Append('%s%spush_back(tmp);' % (dst_var, accessor)) 801 .Eblock('}') 802 ) 803 return c 804 805 def _GenerateStringToEnumConversion(self, 806 type_, 807 src_var, 808 dst_var, 809 failure_value): 810 """Returns Code that converts a string type in |src_var| to an enum with 811 type |type_| in |dst_var|. In the generated code, if |src_var| is not 812 a valid enum name then the function will return |failure_value|. 813 """ 814 if type_.property_type != PropertyType.ENUM: 815 raise TypeError(type_) 816 c = Code() 817 enum_as_string = '%s_as_string' % type_.unix_name 818 cpp_type_namespace = '' 819 if type_.namespace != self._namespace: 820 cpp_type_namespace = '%s::' % type_.namespace.unix_name 821 cpp_type_name = self._type_helper.GetCppType(type_) 822 (c.Append('std::string %s;' % enum_as_string) 823 .Sblock('if (!%s->GetAsString(&%s)) {' % (src_var, enum_as_string)) 824 .Concat(self._GenerateError( 825 '"\'%%(key)s\': expected string, got " + ' + 826 self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) 827 .Append('return %s;' % failure_value) 828 .Eblock('}') 829 .Append('%s = %sParse%s(%s);' % (dst_var, 830 cpp_type_namespace, 831 cpp_util.Classname(type_.name), 832 enum_as_string)) 833 .Sblock('if (%s == %s%s) {' % (dst_var, 834 cpp_type_namespace, 835 self._type_helper.GetEnumNoneValue(type_))) 836 .Concat(self._GenerateError( 837 '\"\'%%(key)s\': expected \\"' + 838 '\\" or \\"'.join( 839 enum_value.name 840 for enum_value in self._type_helper.FollowRef(type_).enum_values) + 841 '\\", got \\"" + %s + "\\""' % enum_as_string)) 842 .Append('return %s;' % failure_value) 843 .Eblock('}') 844 .Substitute({'src_var': src_var, 'key': type_.name}) 845 ) 846 return c 847 848 def _GeneratePropertyFunctions(self, namespace, params): 849 """Generates the member functions for a list of parameters. 850 """ 851 return self._GenerateTypes(namespace, (param.type_ for param in params)) 852 853 def _GenerateTypes(self, namespace, types): 854 """Generates the member functions for a list of types. 855 """ 856 c = Code() 857 for type_ in types: 858 c.Cblock(self._GenerateType(namespace, type_)) 859 return c 860 861 def _GenerateEnumToString(self, cpp_namespace, type_): 862 """Generates ToString() which gets the string representation of an enum. 863 """ 864 c = Code() 865 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 866 867 if cpp_namespace is not None: 868 c.Append('// static') 869 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace 870 871 c.Sblock('std::string %sToString(%s enum_param) {' % 872 (maybe_namespace, classname)) 873 c.Sblock('switch (enum_param) {') 874 for enum_value in self._type_helper.FollowRef(type_).enum_values: 875 (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value)) 876 .Append(' return "%s";' % enum_value.name)) 877 (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) 878 .Append(' return "";') 879 .Eblock('}') 880 .Append('NOTREACHED();') 881 .Append('return "";') 882 .Eblock('}') 883 ) 884 return c 885 886 def _GenerateEnumFromString(self, cpp_namespace, type_): 887 """Generates FromClassNameString() which gets an enum from its string 888 representation. 889 """ 890 c = Code() 891 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) 892 893 if cpp_namespace is not None: 894 c.Append('// static') 895 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace 896 897 c.Sblock('%s%s %sParse%s(const std::string& enum_string) {' % 898 (maybe_namespace, classname, maybe_namespace, classname)) 899 for i, enum_value in enumerate( 900 self._type_helper.FollowRef(type_).enum_values): 901 # This is broken up into all ifs with no else ifs because we get 902 # "fatal error C1061: compiler limit : blocks nested too deeply" 903 # on Windows. 904 (c.Append('if (enum_string == "%s")' % enum_value.name) 905 .Append(' return %s;' % 906 self._type_helper.GetEnumValue(type_, enum_value))) 907 (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) 908 .Eblock('}') 909 ) 910 return c 911 912 def _GenerateCreateCallbackArguments(self, function_scope, callback): 913 """Generate all functions to create Value parameters for a callback. 914 915 E.g for function "Bar", generate Bar::Results::Create 916 E.g for event "Baz", generate Baz::Create 917 918 function_scope: the function scope path, e.g. Foo::Bar for the function 919 Foo::Bar::Baz(). May be None if there is no function scope. 920 callback: the Function object we are creating callback arguments for. 921 """ 922 c = Code() 923 params = callback.params 924 c.Concat(self._GeneratePropertyFunctions(function_scope, params)) 925 926 (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s' 927 'Create(%(declaration_list)s) {') 928 .Append('scoped_ptr<base::ListValue> create_results(' 929 'new base::ListValue());') 930 ) 931 declaration_list = [] 932 for param in params: 933 declaration_list.append(cpp_util.GetParameterDeclaration( 934 param, self._type_helper.GetCppType(param.type_))) 935 c.Append('create_results->Append(%s);' % 936 self._CreateValueFromType(param.type_, param.unix_name)) 937 c.Append('return create_results.Pass();') 938 c.Eblock('}') 939 c.Substitute({ 940 'function_scope': ('%s::' % function_scope) if function_scope else '', 941 'declaration_list': ', '.join(declaration_list), 942 'param_names': ', '.join(param.unix_name for param in params) 943 }) 944 return c 945 946 def _GenerateEventNameConstant(self, function_scope, event): 947 """Generates a constant string array for the event name. 948 """ 949 c = Code() 950 c.Append('const char kEventName[] = "%s.%s";' % ( 951 self._namespace.name, event.name)) 952 return c 953 954 def _InitializePropertyToDefault(self, prop, dst): 955 """Initialize a model.Property to its default value inside an object. 956 957 E.g for optional enum "state", generate dst->state = STATE_NONE; 958 959 dst: Type* 960 """ 961 c = Code() 962 underlying_type = self._type_helper.FollowRef(prop.type_) 963 if (underlying_type.property_type == PropertyType.ENUM and 964 prop.optional): 965 c.Append('%s->%s = %s;' % ( 966 dst, 967 prop.unix_name, 968 self._type_helper.GetEnumNoneValue(prop.type_))) 969 return c 970 971 def _GenerateError(self, body): 972 """Generates an error message pertaining to population failure. 973 974 E.g 'expected bool, got int' 975 """ 976 c = Code() 977 if not self._generate_error_messages: 978 return c 979 (c.Append('if (error) {') 980 .Append(' if (error->length())') 981 .Append(' error->append(UTF8ToUTF16("; "));') 982 .Append(' error->append(UTF8ToUTF16(%s));' % body) 983 .Append('}') 984 .Append('else') 985 .Append(' *error = UTF8ToUTF16(%s);' % body)) 986 return c 987 988 def _GenerateParams(self, params): 989 """Builds the parameter list for a function, given an array of parameters. 990 """ 991 if self._generate_error_messages: 992 params = list(params) + ['base::string16* error'] 993 return ', '.join(str(p) for p in params) 994 995 def _GenerateArgs(self, args): 996 """Builds the argument list for a function, given an array of arguments. 997 """ 998 if self._generate_error_messages: 999 args = list(args) + ['error'] 1000 return ', '.join(str(a) for a in args) 1001