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