Roll src/third_party/WebKit 6f84130:7353389 (svn 184386:184391)
[chromium-blink-merge.git] / tools / json_schema_compiler / cc_generator.py
blob1d243f03b5ad30fc582e4fb5083a2367eefe1cb7
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.
5 from code import Code
6 from model import PropertyType
7 import cpp_util
8 import schema_util
9 import util_cc_helper
10 from cpp_namespace_environment import CppNamespaceEnvironment
12 class CCGenerator(object):
13 def __init__(self, type_generator):
14 self._type_generator = type_generator
16 def Generate(self, namespace):
17 return _Generator(namespace, self._type_generator).Generate()
20 class _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)
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)
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))
57 if self._namespace.properties:
58 (c.Append('//')
59 .Append('// Properties')
60 .Append('//')
61 .Append()
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()))
77 if self._namespace.functions:
78 (c.Append('//')
79 .Append('// Functions')
80 .Append('//')
81 .Append()
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()
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
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()
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)
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))
125 (c.Append('%s::%s()' % (classname_in_namespace, classname))
126 .Cblock(self._GenerateInitializersAndBody(type_))
127 .Append('%s::~%s() {}' % (classname_in_namespace, classname))
128 .Append()
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_))
141 return c
143 def _GenerateInitializersAndBody(self, type_):
144 items = []
145 for prop in type_.properties.values():
146 t = prop.type_
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)
176 if items:
177 s = ': %s' % (', '.join(items))
178 else:
179 s = ''
180 s = s + ' {}'
181 return Code().Append(s)
183 def _GenerateTypePopulate(self, cpp_namespace, type_):
184 """Generates the function for populating a type given a pointer to it.
186 E.g for type "Foo", generates Foo::Populate()
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'))))
195 if self._generate_error_messages:
196 c.Append('DCHECK(error);')
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('}')
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('}'))
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('}')
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('}')
262 c.Append('return true;')
263 (c.Eblock('}')
264 .Substitute({'namespace': cpp_namespace, 'name': classname}))
265 return c
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))
275 def _GenerateTypePopulateProperty(self, prop, src, dst):
276 """Generate the code to populate a single property in a type.
278 src: base::DictionaryValue*
279 dst: Type*
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'))
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
312 return c
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',))))
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('}')
330 return c
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()
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_)
343 def _GenerateObjectTypeToValue(self, cpp_namespace, type_):
344 """Generates a function that serializes an object-representing type
345 into a base::DictionaryValue.
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()
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)
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))
377 if prop.optional:
378 c.Eblock('}')
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('}')
402 return (c.Append()
403 .Append('return value.Pass();')
404 .Eblock('}'))
406 def _GenerateChoiceTypeToValue(self, cpp_namespace, type_):
407 """Generates a function that serializes a choice-representing type
408 into a base::Value.
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('}')
424 (c.Append('DCHECK(result) << "Must set at least one choice for %s";' %
425 type_.unix_name)
426 .Append('return result.Pass();')
427 .Eblock('}')
429 return c
431 def _GenerateFunction(self, function):
432 """Generates the definitions for function structs.
434 c = Code()
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()
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))
455 # Results::Create function
456 if function.callback:
457 c.Concat(self._GenerateCreateCallbackArguments(function_namespace,
458 'Results',
459 function.callback))
461 c.Append('} // namespace %s' % function_namespace)
462 return c
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)
476 return c
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.
482 var: variable or variable*
484 E.g for std::string, generate new base::StringValue(var)
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 '.')
499 maybe_namespace = ''
500 if type_.item_type.property_type == PropertyType.REF:
501 maybe_namespace = '%s::' % item_type.namespace.unix_name
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('}'))
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
522 c.Append(code % self._GenerateCreateValueFromType(type_, var, is_ptr))
523 return c
525 def _GenerateCreateValueFromType(self, type_, var, is_ptr):
526 """Generates the statement to create a base::Value given a type.
528 type_: The type of the values being converted.
529 var: The name of the variable.
530 is_ptr: Whether |type_| is optional.
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_))
573 def _GenerateParamsCheck(self, function, var):
574 """Generates a check for the correct number of arguments when creating
575 Params.
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),
599 return c
601 def _GenerateFunctionParamsCreate(self, function):
602 """Generate function to create an instance of Params. The generated
603 function takes a base::ListValue of arguments.
605 E.g for function "Bar", generate Bar::Params::Create()
607 c = Code()
608 (c.Append('// static')
609 .Sblock('scoped_ptr<Params> Params::Create(%s) {' % self._GenerateParams(
610 ['const base::ListValue& args']))
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());')
618 for param in function.params:
619 c.Concat(self._InitializePropertyToDefault(param, 'params'))
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('}')
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()
648 return c
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.
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)
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|.
677 c = Code()
679 underlying_type = self._type_helper.FollowRef(type_)
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));')
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('}')
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;')
730 (c.Append('}')
731 .Append('else')
732 .Append(' %(dst_var)s = temp.Pass();')
733 .Eblock('}')
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('}')
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)))
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();')
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)))
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);')
819 if is_ptr:
820 (c.Append('%(dst_var)s.reset(')
821 .Append(' new std::string(binary_value->GetBuffer(),')
822 .Append(' binary_value->GetSize()));')
824 else:
825 (c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),')
826 .Append(' binary_value->GetSize());')
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('}')
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|.
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('}')
869 return c
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|.
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})
912 return c
914 def _GeneratePropertyFunctions(self, namespace, params):
915 """Generates the member functions for a list of parameters.
917 return self._GenerateTypes(namespace, (param.type_ for param in params))
919 def _GenerateTypes(self, namespace, types):
920 """Generates the member functions for a list of types.
922 c = Code()
923 for type_ in types:
924 c.Cblock(self._GenerateType(namespace, type_))
925 return c
927 def _GenerateEnumToString(self, cpp_namespace, type_):
928 """Generates ToString() which gets the string representation of an enum.
930 c = Code()
931 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
933 if cpp_namespace is not None:
934 c.Append('// static')
935 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace
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('}')
953 return c
955 def _GenerateEnumFromString(self, cpp_namespace, type_):
956 """Generates FromClassNameString() which gets an enum from its string
957 representation.
959 c = Code()
960 classname = cpp_util.Classname(schema_util.StripNamespace(type_.name))
962 if cpp_namespace is not None:
963 c.Append('// static')
964 maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace
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('}')
979 return c
981 def _GenerateCreateCallbackArguments(self,
982 cpp_namespace,
983 function_scope,
984 callback):
985 """Generate all functions to create Value parameters for a callback.
987 E.g for function "Bar", generate Bar::Results::Create
988 E.g for event "Baz", generate Baz::Create
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.
994 c = Code()
995 params = callback.params
996 c.Concat(self._GeneratePropertyFunctions(function_scope, params))
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());')
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)
1018 return c
1020 def _GenerateEventNameConstant(self, function_scope, event):
1021 """Generates a constant string array for the event name.
1023 c = Code()
1024 c.Append('const char kEventName[] = "%s.%s";' % (
1025 self._namespace.name, event.name))
1026 return c
1028 def _InitializePropertyToDefault(self, prop, dst):
1029 """Initialize a model.Property to its default value inside an object.
1031 E.g for optional enum "state", generate dst->state = STATE_NONE;
1033 dst: Type*
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
1045 def _GenerateError(self, body):
1046 """Generates an error message pertaining to population failure.
1048 E.g 'expected bool, got int'
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
1058 def _GenerateParams(self, params):
1059 """Builds the parameter list for a function, given an array of parameters.
1061 if self._generate_error_messages:
1062 params = list(params) + ['base::string16* error']
1063 return ', '.join(str(p) for p in params)
1065 def _GenerateArgs(self, args):
1066 """Builds the argument list for a function, given an array of arguments.
1068 if self._generate_error_messages:
1069 args = list(args) + ['error']
1070 return ', '.join(str(a) for a in args)