cc_generator.py revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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