Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Unified Diff: tools/json_schema_compiler/generate_cc.py

Issue 9114036: Code generation for extensions api (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: some rework Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698