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