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