Chromium Code Reviews| Index: tools/json_schema_compiler/generate_cc.py |
| diff --git a/tools/json_schema_compiler/generate_cc.py b/tools/json_schema_compiler/generate_cc.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5a58a841e84bbb39642c912605af92bef578faa0 |
| --- /dev/null |
| +++ b/tools/json_schema_compiler/generate_cc.py |
| @@ -0,0 +1,287 @@ |
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +from model import PropertyType |
| +import code |
| +import type_manager |
| + |
| +class CC_Generator(object): |
|
not at google - send to devlin
2012/01/13 02:14:09
I like this name, so the file should be called cc_
calamity
2012/01/16 04:01:06
Done.
|
| + """A .cc generator for a namespace.""" |
| + |
| + def __init__(self, namespace, model): |
| + self.type_manager = type_manager.TypeManager(namespace, model) |
| + self.namespace = namespace |
| + |
| + def generate(self): |
| + """Generates the .cc code for a single namespace. |
|
not at google - send to devlin
2012/01/13 02:14:09
"""Generates a code.Code object with the .cc code
calamity
2012/01/16 04:01:06
Done.
|
| + |
| + Returns a code.Code object. |
| + """ |
| + include_path = self.namespace.parent_dir |
| + filename = self.namespace.filename |
| + c = code.Code() |
| + (c.append(code.CHROMIUM_LICENSE).append() |
|
not at google - send to devlin
2012/01/13 02:14:09
put the second .append() on its own line; this wou
calamity
2012/01/16 04:01:06
Done.
|
| + .append(code.WARNING_MESSAGE % self.namespace.parent_path) |
| + .append() |
| + .append('#include "tools/json_schema_compiler/util.h"') |
| + .append('#include "%s/%s.h"' % (include_path, filename)) |
| + # .append('#include "base/json/json_converter.h"') |
|
not at google - send to devlin
2012/01/13 02:14:09
delete
calamity
2012/01/16 04:01:06
Done.
|
| + .append() |
| + .append('namespace extensions {') |
|
not at google - send to devlin
2012/01/13 02:14:09
"extensions" needs to be taken from the command-li
calamity
2012/01/16 04:01:06
Done.
|
| + .append('namespace %s {' % filename) |
| + .append() |
| + .append('//') |
| + .append('// Types') |
| + .append('//') |
| + .append() |
| + ) |
| + for tipe in self.namespace.types.values(): |
| + c.add(self.generate_type(tipe)) |
|
not at google - send to devlin
2012/01/13 02:14:09
pull the last "c.append()" out of generate_type an
calamity
2012/01/16 04:01:06
Done. Also did the same with all other "c.append()
|
| + c.append('//') |
| + c.append('// Functions') |
| + c.append('//') |
| + c.append() |
| + for function in self.namespace.functions.values(): |
| + c.add(self.generate_function(function)) |
| + c.append('} // namespace extensions') |
|
not at google - send to devlin
2012/01/13 02:14:09
ditto extensions
calamity
2012/01/16 04:01:06
Done.
|
| + c.append('} // namespace %s' % filename) |
| + # TODO(calamity): Events |
| + return c |
| + |
| + def generate_type(self, tipe): |
| + """Generates the function definitions for a type in the namespace.""" |
|
not at google - send to devlin
2012/01/13 02:14:09
... for a type.
"""
(no need for "in the namespac
calamity
2012/01/16 04:01:06
Done.
|
| + c = code.Code() |
| + classname = '%s' % (tipe.name) |
|
not at google - send to devlin
2012/01/13 02:14:09
can delete this line and inline it?
...
c.substit
calamity
2012/01/16 04:01:06
Done.
|
| + |
| + c.append('%(classname)s::%(classname)s() {}') |
| + c.append('%(classname)s::~%(classname)s() {}') |
| + c.substitute({'classname': classname}) |
| + c.append() |
| + |
| + c.add(self.generate_type_populate(tipe)) |
| + # TODO(calamity): deal with non-serializable |
| + c.add(self.generate_type_tovalue(tipe)) |
| + c.append() |
| + |
| + return c |
| + |
| + def generate_type_populate(self, tipe): |
| + """Generates the function for populating a type given a pointer to it.""" |
| + c = code.Code() |
| + c.append('// static') |
| + c.sblock('bool %(name)s::Populate(const Value& value, %(name)s* out) {') |
| + c.substitute({'name': tipe.name}) |
| + c.append('if (!value.IsType(Value::TYPE_DICTIONARY))') |
| + c.append(' return false;') |
| + c.append('const DictionaryValue* dict = ' |
| + 'static_cast<const DictionaryValue*>(&value);') |
| + |
| + c.append() |
| + # TODO(calamity): this doesn't even handle single properties. |
| + # add ALL the types |
| + for prop in tipe.properties.values(): |
| + sub = {'name': prop.name} |
| + if prop.type == PropertyType.ARRAY: |
| + if prop.item_type.type == PropertyType.REF: |
| + if prop.optional: |
| + c.append('if (!json_schema_compiler::util::GetOptionalTypes<%(type)s>(*dict,') |
|
not at google - send to devlin
2012/01/13 02:14:09
this line is too long.
this makes me think though
calamity
2012/01/16 04:01:06
Yeah, but as always it's a tradeoff between usabil
not at google - send to devlin
2012/01/17 01:59:24
Not really, it's not global state because it's not
|
| + c.append(' "%(name)s", &out->%(name)s))') |
| + c.append(' return false;') |
| + else: |
| + c.append('if (!json_schema_compiler::util::GetTypes<%(type)s>(*dict,') |
| + c.append(' "%(name)s", &out->%(name)s))') |
| + c.append(' return false;') |
| + sub['type'] = self.type_manager.get_generic_type(prop.item_type) |
| + elif prop.item_type.json_type == 'string': |
| + if prop.optional: |
| + c.append('if (!json_schema_compiler::util::GetOptionalStrings' |
| + '(*dict, "%(name)s", &out->%(name)s))') |
| + c.append(' return false;') |
| + else: |
| + c.append('if (!json_schema_compiler::util::GetStrings' |
| + '(*dict, "%(name)s", &out->%(name)s))') |
| + c.append(' return false;') |
|
calamity
2012/01/16 04:01:06
Clearly this if block is going to grow. On a dev b
not at google - send to devlin
2012/01/17 01:59:24
I'm missing the context for this comment.
|
| + else: |
| + raise NotImplementedError(prop.item_type.json_type) |
| + elif prop.type == PropertyType.FUNDAMENTAL: |
| + c.append('if(!dict->%s)' % get_fundamental_value(prop, |
| + '&out->%s' % prop.name)) |
| + c.append(' return false;') |
| + else: |
| + raise NotImplementedError(prop.type) |
| + c.substitute(sub) |
| + c.append('return true;') |
| + c.eblock('}') |
| + c.append() |
| + return c |
| + |
| + def generate_type_tovalue(self, tipe): |
| + """Generates a function that serializes the type into a |
| + |DictionaryValue|.""" |
| + c = code.Code() |
| + c.sblock('DictionaryValue* %s::ToValue() const {' % tipe.name) |
| + name = tipe.name.lower() |
| + c.append('DictionaryValue* value = new DictionaryValue();') |
| + c.append() |
| + for prop in tipe.properties.values(): |
| + prop_name = name + '_' + prop.name if name else prop.name |
| + this_var = prop.name |
| + c.add(self.create_value_from_property(prop_name, prop, this_var)) |
| + c.append() |
| + c.append('return value;') |
| + |
| + c.eblock('}') |
| + return c |
| + |
| + # TODO(calamity): object and choices proptypes |
| + def create_value_from_property(self, name, prop, var): |
| + """Generates code to serialize a single property in a type.""" |
| + c = code.Code() |
| + if prop.type == PropertyType.FUNDAMENTAL: |
| + c.append( |
| + 'Value* %s_value = %s;' % (name, create_fundamental_value(prop, var))) |
| + elif prop.type == PropertyType.ARRAY: |
| + if prop.item_type.json_type == 'string': |
| + if prop.optional: |
| + c.append('json_schema_compiler::util::SetOptionalStrings(%s, "%s", value);' |
| + % (var, prop.name)) |
| + else: |
| + c.append('json_schema_compiler::util::SetStrings(%s, "%s", value);' % |
| + (var, prop.name)) |
| + else: |
| + item_name = name + '_single' |
| + c.append('ListValue* %(name)s_value = new ListValue();') |
| + c.append('for (%(it_type)s::iterator it = %(var)s->begin();') |
| + c.sblock(' it != %(var)s->end(); ++it) {') |
| + c.add(self.create_value_from_property(item_name, prop.item_type, '*it')) |
| + c.append('%(name)s_value->Append(%(prop_val)s_value);') |
| + c.substitute( |
| + {'it_type': self.type_manager.get_type(prop), |
| + 'name': name, 'var': var, 'prop_val': item_name}) |
| + c.eblock('}') |
| + elif prop.type == PropertyType.REF: |
| + c.append('Value* %s_value = %s.ToValue();' % (name, var)) |
| + else: |
| + raise NotImplementedError |
| + return c |
| + |
| + def generate_function(self, function): |
| + """Generates the definitions for function structs.""" |
| + classname = code.cpp_name(function.name) |
| + c = code.Code() |
| + |
| + # Params::Populate function |
| + if function.params: |
| + c.append('%(name)s::Params::Params() {}') |
| + c.append('%(name)s::Params::~Params() {}') |
| + c.append() |
| + c.add(self.generate_function_params_populate(function)) |
| + |
| + # Result::Create function |
| + c.add(self.generate_function_result_create(function)) |
| + |
| + c.substitute({'name': classname}) |
| + |
| + return c |
| + |
| + def generate_function_params_populate(self, function): |
| + """Generate function to populate an instance of Params given a pointer.""" |
| + classname = code.cpp_name(function.name) |
| + c = code.Code() |
| + c.append('// static') |
| + c.append('bool %(classname)s::Params::Populate(const ListValue& args,') |
| + c.sblock(' %(classname)s::Params* out) {') |
| + c.substitute({'classname': classname}) |
| + c.append('if (args.GetSize() != %d)' % len(function.params)) |
| + c.append(' return false;') |
| + |
| + # TODO(calamity): generalize, needs to move to function to do populates for |
| + # wider variety of args |
| + for i, param in enumerate(function.params): |
| + sub = {'name': param.name, 'pos': i} |
| + c.append() |
| + # TODO(calamity): Make valid for not just objects |
| + c.append('DictionaryValue* %(name)s_param = NULL;') |
| + c.append('if (!args.GetDictionary(%(pos)d, &%(name)s_param))') |
| + c.append(' return false;') |
| + if param.type == PropertyType.REF: |
| + c.append('if (!%(ctype)s::Populate(*%(name)s_param, &out->%(name)s))') |
| + c.append(' return false;') |
| + sub['ctype'] = self.type_manager.get_type(param) |
| + elif param.type == PropertyType.FUNDAMENTAL: |
| + #XXX THIS IS WRONG |
| + c.append('// TODO Needs some sort of casting') |
| + c.append('if (!%(name)s_param->' + |
| + get_fundamental_value(param,'&out->%s' % param.name) +');') |
| + c.append(' return false;') |
| + elif param.type == PropertyType.OBJECT: |
| + c.append('if(!%(ctype)s::Populate(*%(name)s_param, &out->%(name)s))') |
| + c.append(' return false;') |
| + sub['ctype'] = self.type_manager.get_type(param) |
| + elif param.type == PropertyType.CHOICES: |
| + c.append('// TODO handle chocies') |
| + else: |
| + raise NotImplementedError(param.type) |
| + c.substitute(sub) |
| + c.append() |
| + c.append('return true;') |
| + c.eblock('}') |
| + c.append() |
| + |
| + return c |
| + |
| + def generate_function_result_create(self, function): |
| + """Generate function to create a Result given the return value.""" |
| + classname = code.cpp_name(function.name) |
| + c = code.Code() |
| + c.append('// static') |
| + param = function.callback.param |
| + arg = '' |
| + if param: |
| + arg = 'const ' + self.type_manager.parameter_declaration(param, |
| + type_modifiers={PropertyType.REF: type_manager.ParamFormat.REFERENCE}) |
| + c.sblock('Value* %(classname)s::Result::Create(%(arg)s) {') |
| + sub = {'classname': classname, 'arg': arg} |
| + # TODO(calamity): Choices |
| + if not param: |
| + c.append('return Value::CreateNullValue();') |
| + else: |
| + sub['argname'] = param.name |
| + if param.type == PropertyType.FUNDAMENTAL: |
| + c.append('return %s;' % create_fundamental_value(param, param.name)) |
| + elif param.type == PropertyType.REF: |
| + c.append('DictionaryValue* result = new DictionaryValue();') |
| + c.append('result->Set("%(argname)s", %(argname)s.ToValue());') |
|
not at google - send to devlin
2012/01/13 02:14:09
SetWithoutPathExpansion
(need to audit this whole
calamity
2012/01/16 04:01:06
Done.
|
| + c.append('return result;') |
| + elif param.type == PropertyType.OBJECT: |
| + c.append('// TODO object stuff') |
| + elif param.type == PropertyType.ARRAY: |
| + c.append('// TODO array stuff') |
| + else: |
| + raise NotImplementedError(param.type) |
| + c.substitute(sub) |
| + c.eblock('}') |
| + c.append() |
| + return c |
| + |
| +def create_fundamental_value(prop, var): |
| + """Returns the C++ code for creating a value of the given property type |
| + using the given variable.""" |
| + return { |
| + 'string': 'Value::CreateStringValue(%s)', |
| + 'boolean': 'Value::CreateBooleanValue(%s)', |
| + 'integer': 'Value::CreateIntegerValue(%s)', |
| + 'double': 'Value::CreateDoubleValue(%s)', |
| + }[prop.json_type] % var |
| + |
| + |
| +def get_fundamental_value(prop, var): |
| + """Returns the C++ code for retrieving a fundamental type from a Value |
| + into a variable.""" |
| + return { |
| + 'string': 'GetAsString(%s)', |
| + 'boolean': 'GetAsBoolean(%s)', |
| + 'integer': 'GetAsInteger(%s)', |
| + 'double': 'GetAsDouble(%s)', |
| + }[prop.json_type] % var |