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