diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-17 06:50:31 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-17 06:50:31 +0000 |
commit | 242d5e7aaf2658a88f1ed4954e34876d7082f1a7 (patch) | |
tree | 677cff28f304753c8e9f1d1e9062a9b2de871a77 /tools | |
parent | 3543975945a5667aa3fb48c6d0e1b6312d1ed293 (diff) | |
download | chromium_src-242d5e7aaf2658a88f1ed4954e34876d7082f1a7.zip chromium_src-242d5e7aaf2658a88f1ed4954e34876d7082f1a7.tar.gz chromium_src-242d5e7aaf2658a88f1ed4954e34876d7082f1a7.tar.bz2 |
Overhaul JSON Schema Compiler to support a number of features required to
support the full range of JSON/IDL features, improve the interface of generated
code, and improve generated code formatting, notably:
* Make "choices" generate a struct containing each possible type, rather than
multiple fields with different names plus and enum. This makes it possible
to embed choices inside containers - notably within arrays (as in bug).
It also improves optional choices detection.
* Remove the Any C++ type generated by "any", and model it as a base::Value.
* Make "additionalProperties" generated a std::map with the actual types as
values, rather than just a DictionaryValue forcing callers to extract types.
* Fix enum naming, it was unnecessarily including the namespace as a prefix.
R=yoz@chromium.org
BUG=162044
Review URL: https://codereview.chromium.org/11827026
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177361 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
32 files changed, 1243 insertions, 1734 deletions
diff --git a/tools/json_schema_compiler/any.cc b/tools/json_schema_compiler/any.cc deleted file mode 100644 index cd4e5c3..0000000 --- a/tools/json_schema_compiler/any.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2012 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. - -#include "tools/json_schema_compiler/any.h" - -#include "base/logging.h" -#include "base/values.h" - -namespace json_schema_compiler { -namespace any { - -Any::Any() {} - -Any::Any(scoped_ptr<base::Value> from_value) - : value_(from_value.Pass()) { -} - -Any::~Any() {} - -void Any::Init(const base::Value& from_value) { - CHECK(!value_.get()); - value_.reset(from_value.DeepCopy()); -} - -const base::Value& Any::value() const { - CHECK(value_.get()); - return *value_; -} - -} // namespace any -} // namespace json_schema_compiler diff --git a/tools/json_schema_compiler/any.h b/tools/json_schema_compiler/any.h deleted file mode 100644 index 83b84d6..0000000 --- a/tools/json_schema_compiler/any.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef TOOLS_JSON_SCHEMA_COMPILER_ANY_H__ -#define TOOLS_JSON_SCHEMA_COMPILER_ANY_H__ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" - -namespace base { -class Value; -} - -namespace json_schema_compiler { -namespace any { - -// Represents an "any" type in JSON schema as a wrapped Value. -class Any { - public: - Any(); - explicit Any(scoped_ptr<base::Value> from_value); - ~Any(); - - // Initializes the Value in this Any. Fails if already initialized. - void Init(const base::Value& from_value); - - // Get the Value from this Any. - const base::Value& value() const; - - private: - scoped_ptr<base::Value> value_; - - DISALLOW_COPY_AND_ASSIGN(Any); -}; - -} // namespace any -} // namespace json_schema_compiler - -#endif // TOOLS_JSON_SCHEMA_COMPILER_ANY_H__ diff --git a/tools/json_schema_compiler/any_helper.py b/tools/json_schema_compiler/any_helper.py deleted file mode 100644 index a087ffd..0000000 --- a/tools/json_schema_compiler/any_helper.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2012 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. - -ANY_NAMESPACE = 'json_schema_compiler::any' -ANY_CLASS = ANY_NAMESPACE + '::Any' - -class AnyHelper(object): - """A util class that generates code that uses - tools/json_schema_compiler/any.cc. - """ - def Init(self, any_prop, src, dst): - """Initialize |dst|.|any_prop| to |src|. - - src: Value* - dst: Type* - """ - if any_prop.optional: - return '%s->%s->Init(*%s)' % (dst, any_prop.unix_name, src) - else: - return '%s->%s.Init(*%s)' % (dst, any_prop.unix_name, src) - - def GetValue(self, any_prop, var): - """Get |var| as a const Value&. - - var: Any* or Any - """ - if any_prop.optional: - return '%s->value()' % var - else: - return '%s.value()' % var - diff --git a/tools/json_schema_compiler/api_gen_util.gyp b/tools/json_schema_compiler/api_gen_util.gyp index b1c220d..54966cc 100644 --- a/tools/json_schema_compiler/api_gen_util.gyp +++ b/tools/json_schema_compiler/api_gen_util.gyp @@ -10,7 +10,6 @@ 'target_name': 'api_gen_util', 'type': 'static_library', 'sources': [ - 'any.cc', 'util.cc', ], 'dependencies': ['<(DEPTH)/base/base.gyp:base'], diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py index 2e42998..86724fc 100644 --- a/tools/json_schema_compiler/cc_generator.py +++ b/tools/json_schema_compiler/cc_generator.py @@ -3,8 +3,7 @@ # found in the LICENSE file. from code import Code -from model import PropertyType -import any_helper +from model import PropertyType, Type import cpp_util import model import schema_util @@ -15,13 +14,12 @@ class CCGenerator(object): """A .cc generator for a namespace. """ def __init__(self, namespace, cpp_type_generator): - self._cpp_type_generator = cpp_type_generator + self._type_helper = cpp_type_generator self._namespace = namespace self._target_namespace = ( - self._cpp_type_generator.GetCppNamespaceName(self._namespace)) + self._type_helper.GetCppNamespaceName(self._namespace)) self._util_cc_helper = ( - util_cc_helper.UtilCCHelper(self._cpp_type_generator)) - self._any_helper = any_helper.AnyHelper() + util_cc_helper.UtilCCHelper(self._type_helper)) def Generate(self): """Generates a Code object with the .cc for a single namespace. @@ -34,17 +32,10 @@ class CCGenerator(object): .Append(self._util_cc_helper.GetIncludePath()) .Append('#include "%s/%s.h"' % (self._namespace.source_file_dir, self._namespace.unix_name)) - ) - includes = self._cpp_type_generator.GenerateIncludes() - if not includes.IsEmpty(): - (c.Concat(includes) - .Append() - ) - - (c.Append() - .Concat(self._cpp_type_generator.GetRootNamespaceStart()) - .Concat(self._cpp_type_generator.GetNamespaceStart()) - .Append() + .Append('#include "base/logging.h"') + .Cblock(self._type_helper.GenerateIncludes()) + .Concat(self._type_helper.GetRootNamespaceStart()) + .Cblock(self._type_helper.GetNamespaceStart()) ) if self._namespace.properties: (c.Append('//') @@ -53,21 +44,18 @@ class CCGenerator(object): .Append() ) for property in self._namespace.properties.values(): - property_code = self._cpp_type_generator.GeneratePropertyValues( + property_code = self._type_helper.GeneratePropertyValues( property, 'const %(type)s %(name)s = %(value)s;', nodoc=True) if property_code: - c.Concat(property_code).Append() + c.Cblock(property_code) if self._namespace.types: (c.Append('//') .Append('// Types') .Append('//') .Append() - ) - for type_ in self._namespace.types.values(): - (c.Concat(self._GenerateType( - schema_util.StripSchemaNamespace(type_.name), type_)).Append() + .Cblock(self._GenerateTypes(None, self._namespace.types.values())) ) if self._namespace.functions: (c.Append('//') @@ -76,24 +64,17 @@ class CCGenerator(object): .Append() ) for function in self._namespace.functions.values(): - (c.Concat(self._GenerateFunction( - cpp_util.Classname(function.name), function)) - .Append() - ) + c.Cblock(self._GenerateFunction(function)) if self._namespace.events: (c.Append('//') .Append('// Events') .Append('//') .Append() ) - for event in self._namespace.events.values(): - (c.Concat(self._GenerateCreateCallbackArguments( - cpp_util.Classname(event.name), event, generate_to_json=True)) - .Append() - ) - (c.Concat(self._cpp_type_generator.GetNamespaceEnd()) - .Concat(self._cpp_type_generator.GetRootNamespaceEnd()) - .Append() + for event in self._namespace.events.values(): + c.Cblock(self._GenerateEvent(event)) + (c.Concat(self._type_helper.GetNamespaceEnd()) + .Cblock(self._type_helper.GetRootNamespaceEnd()) ) return c @@ -104,33 +85,40 @@ class CCGenerator(object): c = Code() if type_.functions: - for function in type_.functions.values(): - (c.Concat( - self._GenerateFunction( - cpp_namespace + '::' + cpp_util.Classname(function.name), - function)) - .Append()) - elif type_.type_ == PropertyType.OBJECT: - (c.Concat(self._GeneratePropertyFunctions( - cpp_namespace, type_.properties.values())) - .Sblock('%(namespace)s::%(classname)s()') - .Concat(self._GenerateInitializersAndBody(type_)) - .Eblock('%(namespace)s::~%(classname)s() {}') + # Wrap functions within types in the type's namespace. + (c.Append('namespace %s {' % classname) .Append()) - if type_.from_json: - (c.Concat(self._GenerateTypePopulate(cpp_namespace, type_)) - .Append()) - if type_.from_client: - (c.Concat(self._GenerateTypeToValue(cpp_namespace, type_)) - .Append()) - elif self._cpp_type_generator.IsEnumOrEnumRef(type_): - (c.Concat(self._GenerateCreateEnumTypeValue(cpp_namespace, type_)) - .Append() - .Concat(self._GenerateEnumFromString(cpp_namespace, type_)) + for function in type_.functions.values(): + c.Cblock(self._GenerateFunction(function)) + c.Append('} // namespace %s' % classname) + elif type_.property_type == PropertyType.ARRAY: + c.Cblock(self._GenerateType(cpp_namespace, type_.item_type)) + elif (type_.property_type == PropertyType.OBJECT or + type_.property_type == PropertyType.CHOICES): + if cpp_namespace is None: + classname_in_namespace = classname + else: + classname_in_namespace = '%s::%s' % (cpp_namespace, classname) + + if type_.property_type == PropertyType.OBJECT: + c.Cblock(self._GeneratePropertyFunctions(classname_in_namespace, + type_.properties.values())) + else: + c.Cblock(self._GenerateTypes(classname_in_namespace, type_.choices)) + + (c.Append('%s::%s()' % (classname_in_namespace, classname)) + .Cblock(self._GenerateInitializersAndBody(type_)) + .Append('%s::~%s() {}' % (classname_in_namespace, classname)) .Append() - .Concat(self._GenerateEnumToString(cpp_namespace, type_)) - .Append()) - c.Substitute({'classname': classname, 'namespace': cpp_namespace}) + ) + if type_.origin.from_json: + c.Cblock(self._GenerateTypePopulate(classname_in_namespace, type_)) + if type_.origin.from_client: + c.Cblock(self._GenerateTypeToValue(classname_in_namespace, type_)) + elif type_.property_type == PropertyType.ENUM: + (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_)) + .Cblock(self._GenerateEnumFromString(cpp_namespace, type_)) + ) return c @@ -141,29 +129,28 @@ class CCGenerator(object): continue t = prop.type_ - if t == PropertyType.INTEGER: + if t.property_type == PropertyType.INTEGER: items.append('%s(0)' % prop.unix_name) - elif t == PropertyType.DOUBLE: + elif t.property_type == PropertyType.DOUBLE: items.append('%s(0.0)' % prop.unix_name) - elif t == PropertyType.BOOLEAN: + elif t.property_type == PropertyType.BOOLEAN: items.append('%s(false)' % prop.unix_name) - elif t == PropertyType.BINARY: + elif t.property_type == PropertyType.BINARY: items.append('%s(NULL)' % prop.unix_name) - elif (t == PropertyType.ADDITIONAL_PROPERTIES or - t == PropertyType.ANY or - t == PropertyType.ARRAY or - t == PropertyType.CHOICES or - t == PropertyType.ENUM or - t == PropertyType.OBJECT or - t == PropertyType.FUNCTION or - t == PropertyType.REF or - t == PropertyType.STRING): + elif (t.property_type == PropertyType.ANY or + t.property_type == PropertyType.ARRAY or + t.property_type == PropertyType.CHOICES or + t.property_type == PropertyType.ENUM or + t.property_type == PropertyType.OBJECT or + t.property_type == PropertyType.FUNCTION or + t.property_type == PropertyType.REF or + t.property_type == PropertyType.STRING): # TODO(miket): It would be nice to initialize CHOICES and ENUM, but we # don't presently have the semantics to indicate which one of a set # should be the default. continue else: - sys.exit("Unhandled PropertyType: %s" % t) + raise TypeError(t) if items: s = ': %s' % (', '.join(items)) @@ -180,33 +167,54 @@ class CCGenerator(object): classname = cpp_util.Classname(schema_util.StripSchemaNamespace(type_.name)) c = Code() (c.Append('// static') - .Sblock('bool %(namespace)s::Populate' - '(const base::Value& value, %(name)s* out) {') - .Append('if (!value.IsType(base::Value::TYPE_DICTIONARY))') - .Append(' return false;') + .Append('bool %(namespace)s::Populate(') + .Sblock(' const base::Value& value, %(name)s* out) {') ) - if type_.properties: - (c.Append('const base::DictionaryValue* dict = ' - 'static_cast<const base::DictionaryValue*>(&value);') - .Append() + if type_.property_type == PropertyType.CHOICES: + for choice in type_.choices: + value_type = cpp_util.GetValueType(self._type_helper.FollowRef(choice)) + (c.Sblock('if (value.IsType(%s)) {' % value_type) + .Concat(self._GeneratePopulateVariableFromValue( + choice, + '(&value)', + 'out->as_%s' % choice.unix_name, + 'false', + is_ptr=True)) + .Append('return true;') + .Eblock('}') + ) + c.Append('return false;') + elif type_.property_type == PropertyType.OBJECT: + (c.Append('if (!value.IsType(base::Value::TYPE_DICTIONARY))') + .Append(' return false;') ) + if type_.properties or type_.additional_properties is not None: + c.Append('const base::DictionaryValue* dict = ' + 'static_cast<const base::DictionaryValue*>(&value);') for prop in type_.properties.values(): c.Concat(self._InitializePropertyToDefault(prop, 'out')) for prop in type_.properties.values(): - if prop.type_ == PropertyType.ADDITIONAL_PROPERTIES: + c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) + if type_.additional_properties is not None: + if type_.additional_properties.property_type == PropertyType.ANY: c.Append('out->additional_properties.MergeDictionary(dict);') - # remove all keys that are actual properties - for cur_prop in type_.properties.values(): - if prop != cur_prop: - c.Append('out->additional_properties' - '.RemoveWithoutPathExpansion("%s", NULL);' % cur_prop.name) - c.Append() else: - c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) - (c.Append('return true;') - .Eblock('}') - ) - c.Substitute({'namespace': cpp_namespace, 'name': classname}) + cpp_type = self._type_helper.GetCppType(type_.additional_properties, + is_in_container=True) + (c.Append('for (base::DictionaryValue::Iterator it(*dict);') + .Sblock(' it.HasNext(); it.Advance()) {') + .Append('%s tmp;' % cpp_type) + .Concat(self._GeneratePopulateVariableFromValue( + type_.additional_properties, + '(&it.value())', + 'tmp', + 'false')) + .Append('out->additional_properties[it.key()] = tmp;') + .Eblock('}') + ) + c.Append('return true;') + (c.Eblock('}') + .Substitute({'namespace': cpp_namespace, 'name': classname})) return c def _GenerateTypePopulateProperty(self, prop, src, dst): @@ -223,10 +231,11 @@ class CCGenerator(object): 'if (%(src)s->GetWithoutPathExpansion("%(key)s", &%(value_var)s)) {') .Concat(self._GeneratePopulatePropertyFromValue( prop, value_var, dst, 'false'))) - if self._cpp_type_generator.IsEnumOrEnumRef(prop): + underlying_type = self._type_helper.FollowRef(prop.type_) + if underlying_type.property_type == PropertyType.ENUM: (c.Append('} else {') .Append('%%(dst)s->%%(name)s = %s;' % - self._cpp_type_generator.GetEnumNoneValue(prop))) + self._type_helper.GetEnumNoneValue(prop.type_))) c.Eblock('}') else: (c.Append( @@ -246,140 +255,184 @@ class CCGenerator(object): return c def _GenerateTypeToValue(self, cpp_namespace, type_): - """Generates a function that serializes the type into a - |base::DictionaryValue|. - + """Generates a function that serializes the type into a base::Value. E.g. for type "Foo" generates Foo::ToValue() """ + if type_.property_type == PropertyType.OBJECT: + return self._GenerateObjectTypeToValue(cpp_namespace, type_) + elif type_.property_type == PropertyType.CHOICES: + return self._GenerateChoiceTypeToValue(cpp_namespace, type_) + else: + raise ValueError("Unsupported property type %s" % type_.type_) + + def _GenerateObjectTypeToValue(self, cpp_namespace, type_): + """Generates a function that serializes an object-representing type + into a base::DictionaryValue. + """ c = Code() (c.Sblock('scoped_ptr<base::DictionaryValue> %s::ToValue() const {' % cpp_namespace) .Append('scoped_ptr<base::DictionaryValue> value(' - 'new base::DictionaryValue());') + 'new base::DictionaryValue());') .Append() ) + for prop in type_.properties.values(): - if prop.type_ == PropertyType.ADDITIONAL_PROPERTIES: - c.Append('value->MergeDictionary(&%s);' % prop.unix_name) - else: - if prop.optional: - if self._cpp_type_generator.IsEnumOrEnumRef(prop): - c.Sblock('if (%s != %s) {' % - (prop.unix_name, - self._cpp_type_generator.GetEnumNoneValue(prop))) - elif prop.type_ == PropertyType.CHOICES: - c.Sblock('if (%s_type != %s) {' % - (prop.unix_name, - self._cpp_type_generator.GetEnumNoneValue(prop))) - else: - c.Sblock('if (%s.get()) {' % prop.unix_name) - - if prop.type_ == prop.compiled_type: - c.Append('value->SetWithoutPathExpansion("%s", %s);' % ( - prop.name, - self._CreateValueFromProperty(prop, 'this->' + prop.unix_name))) + if prop.optional: + # Optional enum values are generated with a NONE enum value. + underlying_type = self._type_helper.FollowRef(prop.type_) + if underlying_type.property_type == PropertyType.ENUM: + c.Sblock('if (%s != %s) {' % + (prop.unix_name, + self._type_helper.GetEnumNoneValue(prop.type_))) else: - conversion_src = 'this->' + prop.unix_name - if prop.optional: - conversion_src = '*' + conversion_src - (c.Append('%s %s;' % (self._cpp_type_generator.GetType(prop), - prop.unix_name)) - .Append(cpp_util.GenerateCompiledTypeToTypeConversion( - self._cpp_type_generator.GetReferencedProperty(prop), - conversion_src, - prop.unix_name) + ';') - .Append('value->SetWithoutPathExpansion("%s", %s);' % ( - prop.unix_name, - self._CreateValueFromProperty(prop, prop.unix_name))) - ) - if prop.optional: - c.Eblock('}'); - (c.Append() - .Append('return value.Pass();') + c.Sblock('if (%s.get()) {' % prop.unix_name) + + # ANY is a base::Value which is abstract and cannot be a direct member, so + # it will always be a pointer. + is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY + c.Append('value->SetWithoutPathExpansion("%s", %s);' % ( + prop.name, + self._CreateValueFromType(prop.type_, + 'this->%s' % prop.unix_name, + is_ptr=is_ptr))) + + if prop.optional: + c.Eblock('}'); + + if type_.additional_properties is not None: + if type_.additional_properties.property_type == PropertyType.ANY: + c.Append('value->MergeDictionary(&additional_properties);') + else: + # Non-copyable types will be wrapped in a linked_ptr for inclusion in + # maps, so we need to unwrap them. + needs_unwrap = ( + not self._type_helper.IsCopyable(type_.additional_properties)) + cpp_type = self._type_helper.GetCppType(type_.additional_properties, + is_in_container=True) + (c.Sblock('for (std::map<std::string, %s>::const_iterator it =' % + cpp_util.PadForGenerics(cpp_type)) + .Append(' additional_properties.begin();') + .Append(' it != additional_properties.end(); ++it) {') + .Append('value->SetWithoutPathExpansion(it->first, %s);' % + self._CreateValueFromType( + type_.additional_properties, + '%sit->second' % ('*' if needs_unwrap else ''))) + .Eblock('}') + ) + + return (c.Append() + .Append('return value.Pass();') + .Eblock('}')) + + def _GenerateChoiceTypeToValue(self, cpp_namespace, type_): + """Generates a function that serializes a choice-representing type + into a base::Value. + """ + c = Code() + c.Sblock('scoped_ptr<base::Value> %s::ToValue() const {' % cpp_namespace) + c.Append('scoped_ptr<base::Value> result;'); + for choice in type_.choices: + choice_var = 'as_%s' % choice.unix_name + (c.Sblock('if (%s) {' % choice_var) + .Append('DCHECK(!result) << "Cannot set multiple choices for %s";' % + type_.unix_name) + .Append('result.reset(%s);' % + self._CreateValueFromType(choice, '*%s' % choice_var)) + .Eblock('}') + ) + (c.Append('DCHECK(result) << "Must set at least one choice for %s";' % + type_.unix_name) + .Append('return result.Pass();') .Eblock('}') ) return c - def _GenerateFunction(self, cpp_namespace, function): + def _GenerateFunction(self, function): """Generates the definitions for function structs. """ c = Code() + # TODO(kalman): use function.unix_name not Classname. + function_namespace = cpp_util.Classname(function.name) + (c.Append('namespace %s {' % function_namespace) + .Append() + ) + # Params::Populate function if function.params: - c.Concat(self._GeneratePropertyFunctions(cpp_namespace + '::Params', - function.params)) - (c.Append('%(cpp_namespace)s::Params::Params() {}') - .Append('%(cpp_namespace)s::Params::~Params() {}') - .Append() - .Concat(self._GenerateFunctionParamsCreate(cpp_namespace, function)) + c.Concat(self._GeneratePropertyFunctions('Params', function.params)) + (c.Append('Params::Params() {}') + .Append('Params::~Params() {}') .Append() + .Cblock(self._GenerateFunctionParamsCreate(function)) ) # Results::Create function if function.callback: - c.Concat(self._GenerateCreateCallbackArguments( - "%s::Results" % cpp_namespace, function.callback)) + c.Concat(self._GenerateCreateCallbackArguments('Results', + function.callback)) - c.Substitute({'cpp_namespace': cpp_namespace}) + c.Append('} // namespace %s' % function_namespace) + return c + def _GenerateEvent(self, event): + # TODO(kalman): use event.unix_name not Classname. + c = Code() + event_namespace = cpp_util.Classname(event.name) + (c.Append('namespace %s {' % event_namespace) + .Append() + .Cblock(self._GenerateCreateCallbackArguments(None, event)) + .Append('} // namespace %s' % event_namespace) + ) return c - def _CreateValueFromProperty(self, prop, var): - """Creates a base::Value given a property. Generated code passes ownership + def _CreateValueFromType(self, type_, var, is_ptr=False): + """Creates a base::Value given a type. Generated code passes ownership to caller. var: variable or variable* E.g for std::string, generate base::Value::CreateStringValue(var) """ - if prop.type_ == PropertyType.CHOICES: - return 'Get%sChoiceValue().release()' % cpp_util.Classname(prop.name) - elif self._IsObjectOrObjectRef(prop): - if prop.optional: - return '%s->ToValue().release()' % var + underlying_type = self._type_helper.FollowRef(type_) + if (underlying_type.property_type == PropertyType.CHOICES or + underlying_type.property_type == PropertyType.OBJECT): + if is_ptr: + return '(%s)->ToValue().release()' % var else: - return '%s.ToValue().release()' % var - elif prop.type_ == PropertyType.ANY: - return '%s.DeepCopy()' % self._any_helper.GetValue(prop, var) - elif prop.type_ == PropertyType.ADDITIONAL_PROPERTIES: - return '%s.DeepCopy()' % var - elif prop.type_ == PropertyType.FUNCTION: - if prop.optional: - vardot = var + '->' + return '(%s).ToValue().release()' % var + elif (underlying_type.property_type == PropertyType.ANY or + underlying_type.property_type == PropertyType.FUNCTION): + if is_ptr: + vardot = '(%s)->' % var else: - vardot = var + '.' + vardot = '(%s).' % var return '%sDeepCopy()' % vardot - elif self._cpp_type_generator.IsEnumOrEnumRef(prop): + elif underlying_type.property_type == PropertyType.ENUM: return 'base::Value::CreateStringValue(ToString(%s))' % var - elif prop.type_ == PropertyType.BINARY: - if prop.optional: + elif underlying_type.property_type == PropertyType.BINARY: + if is_ptr: vardot = var + '->' else: vardot = var + '.' return ('base::BinaryValue::CreateWithCopiedBuffer(%sdata(), %ssize())' % (vardot, vardot)) - elif self._IsArrayOrArrayRef(prop): + elif underlying_type.property_type == PropertyType.ARRAY: return '%s.release()' % self._util_cc_helper.CreateValueFromArray( - self._cpp_type_generator.GetReferencedProperty(prop), var, - prop.optional) - elif self._IsFundamentalOrFundamentalRef(prop): - # If prop.type != prop.compiled_type, then no asterisk is necessary - # because the target is a local variable and not a dereferenced scoped - # pointer. The asterisk is instead prepended to conversion_src around line - # 273. - if prop.optional and prop.type_ == prop.compiled_type: - var = '*' + var - prop = self._cpp_type_generator.GetReferencedProperty(prop); - return { - PropertyType.STRING: 'base::Value::CreateStringValue(%s)', - PropertyType.BOOLEAN: 'base::Value::CreateBooleanValue(%s)', - PropertyType.INTEGER: 'base::Value::CreateIntegerValue(%s)', - PropertyType.DOUBLE: 'base::Value::CreateDoubleValue(%s)', - }[prop.type_] % var + underlying_type, + var, + is_ptr) + elif underlying_type.property_type.is_fundamental: + if is_ptr: + var = '*%s' % var + if underlying_type.property_type == PropertyType.STRING: + return 'new base::StringValue(%s)' % var + else: + return 'new base::FundamentalValue(%s)' % var else: raise NotImplementedError('Conversion of %s to base::Value not ' - 'implemented' % repr(prop.type_)) + 'implemented' % repr(type_.type_)) def _GenerateParamsCheck(self, function, var): """Generates a check for the correct number of arguments when creating @@ -405,7 +458,7 @@ class CCGenerator(object): }) return c - def _GenerateFunctionParamsCreate(self, cpp_namespace, function): + def _GenerateFunctionParamsCreate(self, function): """Generate function to create an instance of Params. The generated function takes a base::ListValue of arguments. @@ -413,12 +466,11 @@ class CCGenerator(object): """ c = Code() (c.Append('// static') - .Sblock('scoped_ptr<%(cpp_namespace)s::Params> ' - '%(cpp_namespace)s::Params::Create(const base::ListValue& args) {') + .Sblock('scoped_ptr<Params> ' + 'Params::Create(const base::ListValue& args) {') .Concat(self._GenerateParamsCheck(function, 'args')) .Append('scoped_ptr<Params> params(new Params());') ) - c.Substitute({'cpp_namespace': cpp_namespace}) for param in function.params: c.Concat(self._InitializePropertyToDefault(param, 'params')) @@ -432,9 +484,8 @@ class CCGenerator(object): c.Append() value_var = param.unix_name + '_value' (c.Append('const base::Value* %(value_var)s = NULL;') - .Append('if (args.Get(%(i)s, &%(value_var)s) &&\n' - ' !%(value_var)s->IsType(base::Value::TYPE_NULL))') - .Sblock('{') + .Append('if (args.Get(%(i)s, &%(value_var)s) &&') + .Sblock(' !%(value_var)s->IsType(base::Value::TYPE_NULL)) {') .Concat(self._GeneratePopulatePropertyFromValue( param, value_var, 'params', failure_value)) .Eblock('}') @@ -453,466 +504,291 @@ class CCGenerator(object): return c - def _GeneratePopulatePropertyFromValue( - self, prop, value_var, dst, failure_value, check_type=True): - """Generates code to populate a model.Property given a base::Value*. The - existence of data inside the base::Value* is assumed so checks for existence - should be performed before the code this generates. - - prop: the property the code is populating. - value_var: a base::Value* that should represent |prop|. - dst: the object with |prop| as a member. - failure_value: the value to return if |prop| cannot be extracted from - |value_var| - check_type: if true, will check if |value_var| is the correct - base::Value::Type + def _GeneratePopulatePropertyFromValue(self, + prop, + src_var, + dst_class_var, + failure_value): + """Generates code to populate property |prop| of |dst_class_var| (a + pointer) from a Value*. See |_GeneratePopulateVariableFromValue| for + semantics. + """ + return self._GeneratePopulateVariableFromValue(prop.type_, + src_var, + '%s->%s' % (dst_class_var, + prop.unix_name), + failure_value, + is_ptr=prop.optional) + + def _GeneratePopulateVariableFromValue(self, + type_, + src_var, + dst_var, + failure_value, + is_ptr=False): + """Generates code to populate a variable |dst_var| of type |type_| from a + Value* at |src_var|. The Value* is assumed to be non-NULL. In the generated + code, if |dst_var| fails to be populated then Populate will return + |failure_value|. """ c = Code() c.Sblock('{') - if self._IsFundamentalOrFundamentalRef(prop): - self._GenerateFundamentalOrFundamentalRefPopulate(c, prop, value_var, dst) - elif self._IsObjectOrObjectRef(prop): - self._GenerateObjectOrObjectRefPopulate(c, prop) - elif prop.type_ == PropertyType.FUNCTION: - self._GenerateFunctionPopulate(c, prop) - elif prop.type_ == PropertyType.ANY: - self._GenerateAnyPopulate(c, prop, value_var, dst) - elif self._IsArrayOrArrayRef(prop): - self._GenerateArrayOrArrayRefPopulate(c, prop, dst) - elif prop.type_ == PropertyType.CHOICES: - self._GenerateChoicePopulate(c, prop, value_var, dst, failure_value) - elif self._cpp_type_generator.IsEnumOrEnumRef(prop): - self._GenerateEnumPopulate(c, prop, value_var) - elif prop.type_ == PropertyType.BINARY: - self._GenerateBinaryPopulate(c, prop) - else: - raise NotImplementedError(prop.type_) - c.Eblock('}') - sub = { - 'value_var': value_var, - 'name': prop.unix_name, - 'dst': dst, - 'failure_value': failure_value, - } - if prop.type_ not in (PropertyType.CHOICES, PropertyType.ANY): - sub['ctype'] = self._cpp_type_generator.GetType(prop) - sub['compiled_ctype'] = self._cpp_type_generator.GetCompiledType(prop) - sub['value_type'] = cpp_util.GetValueType(self._cpp_type_generator - .GetReferencedProperty(prop).type_) - c.Substitute(sub) - return c + underlying_type = self._type_helper.FollowRef(type_) - def _GenerateFundamentalOrFundamentalRefPopulate(self, - c, - prop, - value_var, - dst): - if prop.optional: - (c.Append('%(ctype)s temp;') - .Append('if (!%s)' % - cpp_util.GetAsFundamentalValue( - self._cpp_type_generator.GetReferencedProperty(prop), - value_var, - '&temp')) - .Append(' return %(failure_value)s;') - ) - if prop.type_ != prop.compiled_type: - (c.Append('%(compiled_ctype)s temp2;') - .Append('if (!%s)' % - cpp_util.GenerateTypeToCompiledTypeConversion( - self._cpp_type_generator.GetReferencedProperty(prop), - 'temp', - 'temp2')) + if underlying_type.property_type.is_fundamental: + if is_ptr: + (c.Append('%(cpp_type)s temp;') + .Append('if (!%s)' % cpp_util.GetAsFundamentalValue( + self._type_helper.FollowRef(type_), src_var, '&temp')) .Append(' return %(failure_value)s;') - .Append('%(dst)s->%(name)s.reset(new %(compiled_ctype)s(temp2));') + .Append('%(dst_var)s.reset(new %(cpp_type)s(temp));') ) else: - c.Append('%(dst)s->%(name)s.reset(new %(ctype)s(temp));') - - else: - if prop.type_ == prop.compiled_type: - assignment_target = '&%s->%s' % (dst, prop.unix_name) + (c.Append('if (!%s)' % cpp_util.GetAsFundamentalValue( + self._type_helper.FollowRef(type_), + src_var, + '&%s' % dst_var)) + .Append(' return %(failure_value)s;') + ) + elif underlying_type.property_type == PropertyType.OBJECT: + if is_ptr: + (c.Append('const base::DictionaryValue* dictionary = NULL;') + .Append('if (!%(src_var)s->GetAsDictionary(&dictionary))') + .Append(' return %(failure_value)s;') + .Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') + .Append('if (!%(cpp_type)s::Populate(*dictionary, temp.get()))') + .Append(' return %(failure_value)s;') + .Append('%(dst_var)s = temp.Pass();') + ) else: - c.Append('%(ctype)s temp;') - assignment_target = '&temp' - (c.Append('if (!%s)' % - cpp_util.GetAsFundamentalValue( - self._cpp_type_generator.GetReferencedProperty(prop), - value_var, - assignment_target)) - .Append(' return %(failure_value)s;') - ) - if prop.type_ != prop.compiled_type: - (c.Append('if (!%s)' % - cpp_util.GenerateTypeToCompiledTypeConversion( - self._cpp_type_generator.GetReferencedProperty(prop), - 'temp', - '%s->%s' % (dst, prop.unix_name))) + (c.Append('const base::DictionaryValue* dictionary = NULL;') + .Append('if (!%(src_var)s->GetAsDictionary(&dictionary))') + .Append(' return %(failure_value)s;') + .Append('if (!%(cpp_type)s::Populate(*dictionary, &%(dst_var)s))') .Append(' return %(failure_value)s;') ) - - def _GenerateObjectOrObjectRefPopulate(self, c, prop): - if prop.optional: - (c.Append('const base::DictionaryValue* dictionary = NULL;') - .Append('if (!%(value_var)s->GetAsDictionary(&dictionary))') - .Append(' return %(failure_value)s;') - .Append('scoped_ptr<%(ctype)s> temp(new %(ctype)s());') - .Append('if (!%(ctype)s::Populate(*dictionary, temp.get()))') - .Append(' return %(failure_value)s;') - .Append('%(dst)s->%(name)s = temp.Pass();') - ) - else: - (c.Append('const base::DictionaryValue* dictionary = NULL;') - .Append('if (!%(value_var)s->GetAsDictionary(&dictionary))') - .Append(' return %(failure_value)s;') - .Append( - 'if (!%(ctype)s::Populate(*dictionary, &%(dst)s->%(name)s))') - .Append(' return %(failure_value)s;') - ) - - def _GenerateFunctionPopulate(self, c, prop): - if prop.optional: - c.Append('%(dst)s->%(name)s.reset(new base::DictionaryValue());') - - def _GenerateAnyPopulate(self, c, prop, value_var, dst): - if prop.optional: - c.Append('%(dst)s->%(name)s.reset(new ' + any_helper.ANY_CLASS + '());') - c.Append(self._any_helper.Init(prop, value_var, dst) + ';') - - def _GenerateArrayOrArrayRefPopulate(self, c, prop, dst): - # util_cc_helper deals with optional and required arrays - (c.Append('const base::ListValue* list = NULL;') - .Append('if (!%(value_var)s->GetAsList(&list))') - .Append(' return %(failure_value)s;')) - if prop.item_type.type_ == PropertyType.ENUM: - self._GenerateListValueToEnumArrayConversion(c, prop) - else: - (c.Append('if (!%s)' % self._util_cc_helper.PopulateArrayFromList( - self._cpp_type_generator.GetReferencedProperty(prop), 'list', - dst + '->' + prop.unix_name, prop.optional)) + elif underlying_type.property_type == PropertyType.FUNCTION: + if is_ptr: + c.Append('%(dst_var)s.reset(new base::DictionaryValue());') + elif underlying_type.property_type == PropertyType.ANY: + c.Append('%(dst_var)s.reset(%(src_var)s->DeepCopy());') + elif underlying_type.property_type == PropertyType.ARRAY: + # util_cc_helper deals with optional and required arrays + (c.Append('const base::ListValue* list = NULL;') + .Append('if (!%(src_var)s->GetAsList(&list))') + .Append(' return %(failure_value)s;')) + item_type = underlying_type.item_type + if item_type.property_type == PropertyType.ENUM: + c.Concat(self._GenerateListValueToEnumArrayConversion( + item_type, + 'list', + dst_var, + failure_value, + is_ptr=is_ptr)) + else: + (c.Append('if (!%s)' % self._util_cc_helper.PopulateArrayFromList( + underlying_type, + 'list', + dst_var, + is_ptr)) + .Append(' return %(failure_value)s;') + ) + elif underlying_type.property_type == PropertyType.CHOICES: + if is_ptr: + (c.Append('scoped_ptr<%(cpp_type)s> temp(new %(cpp_type)s());') + .Append('if (!%(cpp_type)s::Populate(*%(src_var)s, temp.get()))') + .Append(' return %(failure_value)s;') + .Append('%(dst_var)s = temp.Pass();') + ) + else: + (c.Append('if (!%(cpp_type)s::Populate(*%(src_var)s, &%(dst_var)s))') + .Append(' return %(failure_value)s;') + ) + elif underlying_type.property_type == PropertyType.ENUM: + c.Concat(self._GenerateStringToEnumConversion(type_, + src_var, + dst_var, + failure_value)) + elif underlying_type.property_type == PropertyType.BINARY: + (c.Append('if (!%(src_var)s->IsType(%(value_type)s))') .Append(' return %(failure_value)s;') - ) - - def _GenerateChoicePopulate(self, c, prop, value_var, dst, failure_value): - type_var = '%(dst)s->%(name)s_type' - c.Sblock('switch (%(value_var)s->GetType()) {') - for choice in self._cpp_type_generator.ExpandParams([prop]): - (c.Sblock('case %s: {' % cpp_util.GetValueType( - self._cpp_type_generator.GetReferencedProperty(choice).type_)) - .Concat(self._GeneratePopulatePropertyFromValue( - choice, value_var, dst, failure_value, check_type=False)) - .Append('%s = %s;' % - (type_var, - self._cpp_type_generator.GetEnumValue( - prop, choice.type_.name))) - .Append('break;') - .Eblock('}') - ) - (c.Append('default:') - .Append(' return %(failure_value)s;') - ) - c.Eblock('}') - - def _GenerateEnumPopulate(self, c, prop, value_var): - c.Sblock('{') - self._GenerateStringToEnumConversion(c, prop, value_var, 'enum_temp') - c.Append('%(dst)s->%(name)s = enum_temp;') - c.Eblock('}') - - def _GenerateBinaryPopulate(self, c, prop): - (c.Append('if (!%(value_var)s->IsType(%(value_type)s))') - .Append(' return %(failure_value)s;') - .Append('const base::BinaryValue* binary_value =') - .Append(' static_cast<const base::BinaryValue*>(%(value_var)s);') - ) - if prop.optional: - (c.Append('%(dst)s->%(name)s.reset(') - .Append(' new std::string(binary_value->GetBuffer(),') - .Append(' binary_value->GetSize()));') + .Append('const base::BinaryValue* binary_value =') + .Append(' static_cast<const base::BinaryValue*>(%(src_var)s);') ) + if is_ptr: + (c.Append('%(dst_var)s.reset(') + .Append(' new std::string(binary_value->GetBuffer(),') + .Append(' binary_value->GetSize()));') + ) + else: + (c.Append('%(dst_var)s.assign(binary_value->GetBuffer(),') + .Append(' binary_value->GetSize());') + ) else: - (c.Append('%(dst)s->%(name)s.assign(binary_value->GetBuffer(),') - .Append(' binary_value->GetSize());') - ) + raise NotImplementedError(type_) - def _GenerateListValueToEnumArrayConversion(self, c, prop): - """Appends code that converts a ListValue of string contstants to - an array of enums in dst. - Leaves dst, name, and failure_value unsubstituted. + sub = { + 'cpp_type': self._type_helper.GetCppType(type_), + 'src_var': src_var, + 'dst_var': dst_var, + 'failure_value': failure_value, + } - c: the Code object that is being appended to. - prop: the property that the code is populating. + if underlying_type.property_type not in (PropertyType.ANY, + PropertyType.CHOICES): + sub['value_type'] = cpp_util.GetValueType(underlying_type) + + return c.Eblock('}').Substitute(sub) + + def _GenerateListValueToEnumArrayConversion(self, + item_type, + src_var, + dst_var, + failure_value, + is_ptr=False): + """Returns Code that converts a ListValue of string constants from + |src_var| into an array of enums of |type_| in |dst_var|. On failure, + returns |failure_value|. """ + c = Code() accessor = '.' - if prop.optional: - c.Append('%(dst)s->%(name)s.reset(new std::vector<' + ( - self._cpp_type_generator.GetType(prop.item_type) + '>);')) + if is_ptr: accessor = '->' - c.Sblock('for (ListValue::const_iterator it = list->begin(); ' - 'it != list->end(); ++it) {') - self._GenerateStringToEnumConversion( - c, prop.item_type, '(*it)', 'enum_temp') - c.Append('%(dst)s->%(name)s' + accessor + 'push_back(enum_temp);') - c.Eblock('}') - - def _GenerateStringToEnumConversion(self, c, prop, value_var, enum_temp): - """Appends code that converts a string to an enum. - Leaves failure_value unsubstituted. - - c: the code that is appended to. - prop: the property that the code is populating. - value_var: the string value that is being converted. - enum_temp: the name used to store the temporary enum value. - """ - (c.Append('std::string enum_as_string;') - .Append('if (!%s->GetAsString(&enum_as_string))' % value_var) - .Append(' return %(failure_value)s;') - .Append('%(type)s %(enum)s = From%(type)sString(enum_as_string);' % { - 'type': self._cpp_type_generator.GetCompiledType(prop), - 'enum': enum_temp - }) - .Append('if (%s == %s)' % - (enum_temp, self._cpp_type_generator.GetEnumNoneValue(prop))) - .Append(' return %(failure_value)s;')) - - def _GeneratePropertyFunctions(self, param_namespace, params): - """Generate the functions for structures generated by a property such as - CreateEnumValue for ENUMs and Populate/ToValue for Params/Results objects. + cpp_type = self._type_helper.GetCppType(item_type, is_in_container=True) + c.Append('%s.reset(new std::vector<%s>);' % + (dst_var, cpp_util.PadForGenerics(cpp_type))) + (c.Sblock('for (base::ListValue::const_iterator it = %s->begin(); ' + 'it != %s->end(); ++it) {' % (src_var, src_var)) + .Append('%s tmp;' % self._type_helper.GetCppType(item_type)) + .Concat(self._GenerateStringToEnumConversion(item_type, + '(*it)', + 'tmp', + failure_value)) + .Append('%s%spush_back(tmp);' % (dst_var, accessor)) + .Eblock('}') + ) + return c + + def _GenerateStringToEnumConversion(self, + type_, + src_var, + dst_var, + failure_value): + """Returns Code that converts a string type in |src_var| to an enum with + type |type_| in |dst_var|. In the generated code, if |src_var| is not + a valid enum name then the function will return |failure_value|. """ c = Code() - for param in params: - if param.type_ == PropertyType.OBJECT: - c.Concat(self._GenerateType( - param_namespace + '::' + cpp_util.Classname(param.name), - param)) - c.Append() - elif param.type_ == PropertyType.ARRAY: - c.Concat(self._GeneratePropertyFunctions( - param_namespace, [param.item_type])) - elif param.type_ == PropertyType.CHOICES: - c.Concat(self._GeneratePropertyFunctions( - param_namespace, param.choices.values())) - if param.from_client: - c.Concat(self._GenerateGetChoiceValue(param_namespace, param)) - elif param.type_ == PropertyType.ENUM: - (c.Concat(self._GenerateCreateEnumValue(param_namespace, param)) - .Append() - .Concat(self._GenerateEnumFromString(param_namespace, - param, - use_namespace=True)) - .Append() - .Concat(self._GenerateEnumToString(param_namespace, - param, - use_namespace=True)) - .Append()) + enum_as_string = '%s_as_string' % type_.unix_name + (c.Append('std::string %s;' % enum_as_string) + .Append('if (!%s->GetAsString(&%s))' % (src_var, enum_as_string)) + .Append(' return %s;' % failure_value) + .Append('%s = Parse%s(%s);' % (dst_var, + self._type_helper.GetCppType(type_), + enum_as_string)) + .Append('if (%s == %s)' % (dst_var, + self._type_helper.GetEnumNoneValue(type_))) + .Append(' return %s;' % failure_value) + ) return c - def _GenerateGetChoiceValue(self, cpp_namespace, prop): - """Generates Get<Type>ChoiceValue() that returns a scoped_ptr<base::Value> - representing the choice value. + def _GeneratePropertyFunctions(self, namespace, params): + """Generates the member functions for a list of parameters. """ - c = Code() - (c.Sblock('scoped_ptr<base::Value> ' - '%(cpp_namespace)s::Get%(choice)sChoiceValue() const {') - .Sblock('switch (%s_type) {' % prop.unix_name) - .Concat(self._GenerateReturnCase( - self._cpp_type_generator.GetEnumNoneValue(prop), - 'scoped_ptr<base::Value>()'))) - for choice in self._cpp_type_generator.ExpandParams([prop]): - c.Concat(self._GenerateReturnCase( - self._cpp_type_generator.GetEnumValue(prop, choice.type_.name), - 'make_scoped_ptr<base::Value>(%s)' % - self._CreateValueFromProperty(choice, choice.unix_name))) - (c.Eblock('}') - .Append('return scoped_ptr<base::Value>();') - .Eblock('}') - .Append() - .Substitute({ - 'cpp_namespace': cpp_namespace, - 'choice': cpp_util.Classname(prop.name) - }) - ) - return c + return self._GenerateTypes(namespace, (param.type_ for param in params)) - def _GenerateCreateEnumTypeValue(self, cpp_namespace, prop): - """Generates CreateEnumValue() that returns the base::StringValue - representation of an enum type. + def _GenerateTypes(self, namespace, types): + """Generates the member functions for a list of types. """ c = Code() - classname = cpp_util.Classname(schema_util.StripSchemaNamespace(prop.name)) - (c.Sblock('scoped_ptr<base::Value> CreateEnumValue(%s %s) {' % - (classname, classname.lower())) - .Append('std::string enum_temp = ToString(%s);' % classname.lower()) - .Append('if (enum_temp.empty())') - .Append(' return scoped_ptr<base::Value>();') - .Append('return scoped_ptr<base::Value>(' - 'base::Value::CreateStringValue(enum_temp));') - .Eblock('}')) + for type_ in types: + c.Cblock(self._GenerateType(namespace, type_)) return c - def _GenerateEnumToString(self, cpp_namespace, prop, use_namespace=False): + def _GenerateEnumToString(self, cpp_namespace, type_): """Generates ToString() which gets the string representation of an enum. """ c = Code() - classname = cpp_util.Classname(schema_util.StripSchemaNamespace(prop.name)) - if use_namespace: - namespace = '%s::' % cpp_namespace - else: - namespace = '' + classname = cpp_util.Classname(schema_util.StripSchemaNamespace(type_.name)) - (c.Append('// static') - .Sblock('std::string %(namespace)sToString(%(class)s enum_param) {')) - enum_prop = self._cpp_type_generator.GetReferencedProperty(prop) + if cpp_namespace is not None: + c.Append('// static') + maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace + + c.Sblock('std::string %sToString(%s enum_param) {' % + (maybe_namespace, classname)) c.Sblock('switch (enum_param) {') - for enum_value in enum_prop.enum_values: - c.Concat(self._GenerateReturnCase( - self._cpp_type_generator.GetEnumValue(prop, enum_value), - '"%s"' % enum_value)) - (c.Append('case %s:' % self._cpp_type_generator.GetEnumNoneValue(prop)) + for enum_value in self._type_helper.FollowRef(type_).enum_values: + (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value)) + .Append(' return "%s";' % enum_value)) + (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) .Append(' return "";') .Eblock('}') + .Append('NOTREACHED();') .Append('return "";') .Eblock('}') - .Substitute({ - 'namespace': namespace, - 'class': classname - })) + ) return c - def _GenerateEnumFromString(self, cpp_namespace, prop, use_namespace=False): + def _GenerateEnumFromString(self, cpp_namespace, type_): """Generates FromClassNameString() which gets an enum from its string representation. """ c = Code() - classname = cpp_util.Classname(schema_util.StripSchemaNamespace(prop.name)) - if use_namespace: - namespace = '%s::' % cpp_namespace - else: - namespace = '' + classname = cpp_util.Classname(schema_util.StripSchemaNamespace(type_.name)) - (c.Append('// static') - .Sblock('%(namespace)s%(class)s' - ' %(namespace)sFrom%(class)sString(' - 'const std::string& enum_string) {')) - enum_prop = self._cpp_type_generator.GetReferencedProperty(prop) + if cpp_namespace is not None: + c.Append('// static') + maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace + + c.Sblock('%s%s %sParse%s(const std::string& enum_string) {' % + (maybe_namespace, classname, maybe_namespace, classname)) for i, enum_value in enumerate( - self._cpp_type_generator.GetReferencedProperty(prop).enum_values): + self._type_helper.FollowRef(type_).enum_values): # This is broken up into all ifs with no else ifs because we get # "fatal error C1061: compiler limit : blocks nested too deeply" # on Windows. (c.Append('if (enum_string == "%s")' % enum_value) .Append(' return %s;' % - self._cpp_type_generator.GetEnumValue(prop, enum_value))) - (c.Append('return %s;' % - self._cpp_type_generator.GetEnumNoneValue(prop)) + self._type_helper.GetEnumValue(type_, enum_value))) + (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) .Eblock('}') - .Substitute({ - 'namespace': namespace, - 'class': classname - })) - return c - - # TODO(chebert): This is basically the same as GenerateCreateEnumTypeValue(). - # The plan is to phase out the old-style enums, and make all enums into REF - # types. - def _GenerateCreateEnumValue(self, cpp_namespace, prop): - """Generates CreateEnumValue() that returns the base::StringValue - representation of an enum. - """ - c = Code() - (c.Append('// static') - .Sblock('scoped_ptr<base::Value> %(cpp_namespace)s::CreateEnumValue(' - '%(arg)s) {') - .Append('std::string enum_temp = ToString(%s);' % prop.unix_name) - .Append('if (enum_temp.empty())') - .Append(' return scoped_ptr<base::Value>();') - .Append('return scoped_ptr<base::Value>(' - 'base::Value::CreateStringValue(enum_temp));') - .Eblock('}') - .Substitute({ - 'cpp_namespace': cpp_namespace, - 'arg': cpp_util.GetParameterDeclaration( - prop, self._cpp_type_generator.GetType(prop)) - })) - return c - - def _GenerateReturnCase(self, case_value, return_value): - """Generates a single return case for a switch block. - """ - c = Code() - (c.Append('case %s:' % case_value) - .Append(' return %s;' % return_value) ) return c - def _GenerateCreateCallbackArguments(self, - function_scope, - callback, - generate_to_json=False): + def _GenerateCreateCallbackArguments(self, function_scope, callback): """Generate all functions to create Value parameters for a callback. E.g for function "Bar", generate Bar::Results::Create E.g for event "Baz", generate Baz::Create function_scope: the function scope path, e.g. Foo::Bar for the function - Foo::Bar::Baz(). + Foo::Bar::Baz(). May be None if there is no function scope. callback: the Function object we are creating callback arguments for. - generate_to_json: Generate a ToJson method. """ c = Code() params = callback.params - expanded_params = self._cpp_type_generator.ExpandParams(params) - c.Concat(self._GeneratePropertyFunctions(function_scope, expanded_params)) - - param_lists = self._cpp_type_generator.GetAllPossibleParameterLists(params) - for param_list in param_lists: - (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s::' - 'Create(%(declaration_list)s) {') - .Append('scoped_ptr<base::ListValue> create_results(' - 'new base::ListValue());') - ) - declaration_list = [] - for param in param_list: - # We treat this argument as 'required' to avoid wrapping it in a - # scoped_ptr if it's optional. - param_copy = param.Copy() - param_copy.optional = False - declaration_list.append("const %s" % cpp_util.GetParameterDeclaration( - param_copy, self._cpp_type_generator.GetCompiledType(param_copy))) - param_name = param_copy.unix_name - if param_copy.type_ != param_copy.compiled_type: - param_name = 'temp_' + param_name - (c.Append('%s %s;' % (self._cpp_type_generator.GetType(param_copy), - param_name)) - .Append(cpp_util.GenerateCompiledTypeToTypeConversion( - param_copy, - param_copy.unix_name, - param_name) + ';') - ) - c.Append('create_results->Append(%s);' % - self._CreateValueFromProperty(param_copy, param_name)) - - c.Append('return create_results.Pass();') - c.Eblock('}') - if generate_to_json: - c.Append() - (c.Sblock('std::string %(function_scope)s::' - 'ToJson(%(declaration_list)s) {') - .Append('scoped_ptr<base::ListValue> create_results = ' - '%(function_scope)s::Create(%(param_list)s);') - .Append('std::string json;') - .Append('base::JSONWriter::Write(create_results.get(), &json);') - .Append('return json;') - ) - c.Eblock('}') - - c.Substitute({ - 'function_scope': function_scope, - 'declaration_list': ', '.join(declaration_list), - 'param_list': ', '.join(param.unix_name for param in param_list) - }) + c.Concat(self._GeneratePropertyFunctions(function_scope, params)) + (c.Sblock('scoped_ptr<base::ListValue> %(function_scope)s' + 'Create(%(declaration_list)s) {') + .Append('scoped_ptr<base::ListValue> create_results(' + 'new base::ListValue());') + ) + declaration_list = [] + for param in params: + declaration_list.append(cpp_util.GetParameterDeclaration( + param, self._type_helper.GetCppType(param.type_))) + c.Append('create_results->Append(%s);' % + self._CreateValueFromType(param.type_, param.unix_name)) + c.Append('return create_results.Pass();') + c.Eblock('}') + c.Substitute({ + 'function_scope': ('%s::' % function_scope) if function_scope else '', + 'declaration_list': ', '.join(declaration_list), + 'param_names': ', '.join(param.unix_name for param in params) + }) return c def _InitializePropertyToDefault(self, prop, dst): @@ -923,33 +799,11 @@ class CCGenerator(object): dst: Type* """ c = Code() - if (self._cpp_type_generator.IsEnumOrEnumRef(prop) or - prop.type_ == PropertyType.CHOICES): - if prop.optional: - prop_name = prop.unix_name - if prop.type_ == PropertyType.CHOICES: - prop_name = prop.unix_name + '_type' - c.Append('%s->%s = %s;' % ( - dst, - prop_name, - self._cpp_type_generator.GetEnumNoneValue(prop))) + underlying_type = self._type_helper.FollowRef(prop.type_) + if (underlying_type.property_type == PropertyType.ENUM and + prop.optional): + c.Append('%s->%s = %s;' % ( + dst, + prop.unix_name, + self._type_helper.GetEnumNoneValue(prop.type_))) return c - - def _IsObjectOrObjectRef(self, prop): - """Determines if this property is an Object or is a ref to an Object. - """ - return (self._cpp_type_generator.GetReferencedProperty(prop).type_ == - PropertyType.OBJECT) - - def _IsArrayOrArrayRef(self, prop): - """Determines if this property is an Array or is a ref to an Array. - """ - return (self._cpp_type_generator.GetReferencedProperty(prop).type_ == - PropertyType.ARRAY) - - def _IsFundamentalOrFundamentalRef(self, prop): - """Determines if this property is a Fundamental type or is a ref to a - Fundamental type. - """ - return (self._cpp_type_generator.GetReferencedProperty(prop).type_. - is_fundamental) diff --git a/tools/json_schema_compiler/code.py b/tools/json_schema_compiler/code.py index e4326d4..009eb12 100644 --- a/tools/json_schema_compiler/code.py +++ b/tools/json_schema_compiler/code.py @@ -56,6 +56,13 @@ class Code(object): return self + def Cblock(self, code): + """Concatenates another Code object |code| onto this one followed by a + blank line, if |code| is non-empty.""" + if not code.IsEmpty(): + self.Concat(code).Append() + return self + def Sblock(self, line=''): """Starts a code block. diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py index deade36..296eb41 100644 --- a/tools/json_schema_compiler/cpp_type_generator.py +++ b/tools/json_schema_compiler/cpp_type_generator.py @@ -3,8 +3,7 @@ # found in the LICENSE file. from code import Code -from model import PropertyType -import any_helper +from model import Namespace, PropertyType, Type import cpp_util import operator import schema_util @@ -24,42 +23,22 @@ class CppTypeGenerator(object): if namespace and cpp_namespace: self._namespace = namespace self.AddNamespace(namespace, cpp_namespace) + else: + self._namespace = None def AddNamespace(self, namespace, cpp_namespace): """Maps a model.Namespace to its C++ namespace name. All mappings are beneath the root namespace. """ - for type_ in namespace.types: - if type_ in self._type_namespaces: - raise ValueError('Type %s is declared in both %s and %s' % - (type_, namespace.name, self._type_namespaces[type_].name)) - self._type_namespaces[type_] = namespace self._cpp_namespaces[namespace] = cpp_namespace - - def ExpandParams(self, params): - """Returns the given parameters with PropertyType.CHOICES parameters - expanded so that each choice is a separate parameter. - """ - expanded = [] - for param in params: - if param.type_ == PropertyType.CHOICES: - for choice in param.choices.values(): - expanded.append(choice) - else: - expanded.append(param) - return expanded - - def GetAllPossibleParameterLists(self, params): - """Returns all possible parameter lists for the given set of parameters. - Every combination of arguments passed to any of the PropertyType.CHOICES - parameters will have a corresponding parameter list returned here. - """ - if not params: - return [[]] - partial_parameter_lists = self.GetAllPossibleParameterLists(params[1:]) - return [[param] + partial_list - for param in self.ExpandParams(params[:1]) - for partial_list in partial_parameter_lists] + for type_name in namespace.types: + # Allow $refs to refer to just 'Type' within namespaces. Otherwise they + # must be qualified with 'namespace.Type'. + type_aliases = ['%s.%s' % (namespace.name, type_name)] + if namespace is self._namespace: + type_aliases.append(type_name) + for alias in type_aliases: + self._type_namespaces[alias] = namespace def GetCppNamespaceName(self, namespace): """Gets the mapped C++ namespace name for the given namespace relative to @@ -95,106 +74,89 @@ class CppTypeGenerator(object): return Code().Append('} // %s' % self.GetCppNamespaceName(self._namespace)) - def GetEnumNoneValue(self, prop): + def GetEnumNoneValue(self, type_): """Gets the enum value in the given model.Property indicating no value has been set. """ - return '%s_NONE' % self.GetReferencedProperty(prop).unix_name.upper() + return '%s_NONE' % self.FollowRef(type_).unix_name.upper() - def GetEnumValue(self, prop, enum_value): + def GetEnumValue(self, type_, enum_value): """Gets the enum value of the given model.Property of the given type. e.g VAR_STRING """ - return '%s_%s' % (self.GetReferencedProperty(prop).unix_name.upper(), + return '%s_%s' % (self.FollowRef(type_).unix_name.upper(), cpp_util.Classname(enum_value.upper())) - def GetChoicesEnumType(self, prop): - """Gets the type of the enum for the given model.Property. - - e.g VarType - """ - return cpp_util.Classname(prop.name) + 'Type' - - def GetType(self, prop, pad_for_generics=False, wrap_optional=False): - return self._GetTypeHelper(prop, pad_for_generics, wrap_optional) - - def GetCompiledType(self, prop, pad_for_generics=False, wrap_optional=False): - return self._GetTypeHelper(prop, pad_for_generics, wrap_optional, - use_compiled_type=True) - - def _GetTypeHelper(self, prop, pad_for_generics=False, wrap_optional=False, - use_compiled_type=False): - """Translates a model.Property into its C++ type. + def GetCppType(self, type_, is_ptr=False, is_in_container=False): + """Translates a model.Property or model.Type into its C++ type. If REF types from different namespaces are referenced, will resolve using self._type_namespaces. - Use pad_for_generics when using as a generic to avoid operator ambiguity. + Use |is_ptr| if the type is optional. This will wrap the type in a + scoped_ptr if possible (it is not possible to wrap an enum). - Use wrap_optional to wrap the type in a scoped_ptr<T> if the Property is - optional. - - Use use_compiled_type when converting from prop.type_ to prop.compiled_type. + Use |is_in_container| if the type is appearing in a collection, e.g. a + std::vector or std::map. This will wrap it in the correct type with spacing. """ cpp_type = None - type_ = prop.type_ if not use_compiled_type else prop.compiled_type - - if type_ == PropertyType.REF: - dependency_namespace = self._ResolveTypeNamespace(prop.ref_type) + if type_.property_type == PropertyType.REF: + ref = type_.ref_type + dependency_namespace = self._ResolveTypeNamespace(ref) if not dependency_namespace: - raise KeyError('Cannot find referenced type: %s' % prop.ref_type) + raise KeyError('Cannot find referenced type: %s' % ref) if self._namespace != dependency_namespace: cpp_type = '%s::%s' % (self._cpp_namespaces[dependency_namespace], - schema_util.StripSchemaNamespace(prop.ref_type)) + schema_util.StripSchemaNamespace(ref)) else: - cpp_type = schema_util.StripSchemaNamespace(prop.ref_type) - elif type_ == PropertyType.BOOLEAN: + cpp_type = schema_util.StripSchemaNamespace(ref) + elif type_.property_type == PropertyType.BOOLEAN: cpp_type = 'bool' - elif type_ == PropertyType.INTEGER: + elif type_.property_type == PropertyType.INTEGER: cpp_type = 'int' - elif type_ == PropertyType.INT64: + elif type_.property_type == PropertyType.INT64: cpp_type = 'int64' - elif type_ == PropertyType.DOUBLE: + elif type_.property_type == PropertyType.DOUBLE: cpp_type = 'double' - elif type_ == PropertyType.STRING: + elif type_.property_type == PropertyType.STRING: cpp_type = 'std::string' - elif type_ == PropertyType.ENUM: - cpp_type = cpp_util.Classname(prop.name) - elif type_ == PropertyType.ADDITIONAL_PROPERTIES: - cpp_type = 'base::DictionaryValue' - elif type_ == PropertyType.ANY: - cpp_type = any_helper.ANY_CLASS - elif type_ == PropertyType.OBJECT: - cpp_type = cpp_util.Classname(prop.name) - elif type_ == PropertyType.FUNCTION: + elif type_.property_type == PropertyType.ENUM: + cpp_type = cpp_util.Classname(type_.name) + elif type_.property_type == PropertyType.ANY: + cpp_type = 'base::Value' + elif (type_.property_type == PropertyType.OBJECT or + type_.property_type == PropertyType.CHOICES): + cpp_type = cpp_util.Classname(type_.name) + elif type_.property_type == PropertyType.FUNCTION: # Functions come into the json schema compiler as empty objects. We can - # record these as empty DictionaryValue's so that we know if the function + # record these as empty DictionaryValues so that we know if the function # was passed in or not. cpp_type = 'base::DictionaryValue' - elif type_ == PropertyType.ARRAY: - item_type = prop.item_type - if item_type.type_ == PropertyType.REF: - item_type = self.GetReferencedProperty(item_type) - if item_type.type_ in ( - PropertyType.REF, PropertyType.ANY, PropertyType.OBJECT): - cpp_type = 'std::vector<linked_ptr<%s> > ' - else: - cpp_type = 'std::vector<%s> ' - cpp_type = cpp_type % self.GetType( - prop.item_type, pad_for_generics=True) - elif type_ == PropertyType.BINARY: + elif type_.property_type == PropertyType.ARRAY: + item_cpp_type = self.GetCppType(type_.item_type, is_in_container=True) + cpp_type = 'std::vector<%s>' % cpp_util.PadForGenerics(item_cpp_type) + elif type_.property_type == PropertyType.BINARY: cpp_type = 'std::string' else: - raise NotImplementedError(type_) + raise NotImplementedError('Cannot get type of %s' % type_.property_type) + + # HACK: optional ENUM is represented elsewhere with a _NONE value, so it + # never needs to be wrapped in pointer shenanigans. + # TODO(kalman): change this - but it's an exceedingly far-reaching change. + if not self.FollowRef(type_).property_type == PropertyType.ENUM: + if is_in_container and (is_ptr or not self.IsCopyable(type_)): + cpp_type = 'linked_ptr<%s>' % cpp_util.PadForGenerics(cpp_type) + elif is_ptr: + cpp_type = 'scoped_ptr<%s>' % cpp_util.PadForGenerics(cpp_type) - # Enums aren't wrapped because C++ won't allow it. Optional enums have a - # NONE value generated instead. - if wrap_optional and prop.optional and not self.IsEnumOrEnumRef(prop): - cpp_type = 'scoped_ptr<%s> ' % cpp_type - if pad_for_generics: - return cpp_type - return cpp_type.strip() + return cpp_type + + def IsCopyable(self, type_): + return not (self.FollowRef(type_).property_type in (PropertyType.ANY, + PropertyType.ARRAY, + PropertyType.OBJECT, + PropertyType.CHOICES)) def GenerateForwardDeclarations(self): """Returns the forward declarations for self._namespace. @@ -207,24 +169,17 @@ class CppTypeGenerator(object): for namespace in sorted(namespace_type_dependencies.keys(), key=operator.attrgetter('name')): c.Append('namespace %s {' % namespace.name) - for type_ in sorted(namespace_type_dependencies[namespace], - key=schema_util.StripSchemaNamespace): - type_name = schema_util.StripSchemaNamespace(type_) - if namespace.types[type_].type_ == PropertyType.STRING: - c.Append('typedef std::string %s;' % type_name) - elif namespace.types[type_].type_ == PropertyType.ARRAY: - c.Append('typedef std::vector<%(item_type)s> %(name)s;') - c.Substitute({ - 'name': type_name, - 'item_type': self.GetType(namespace.types[type_].item_type, - wrap_optional=True)}) - # Enums cannot be forward declared. - elif namespace.types[type_].type_ != PropertyType.ENUM: - c.Append('struct %s;' % type_name) + for type_name in sorted(namespace_type_dependencies[namespace], + key=schema_util.StripSchemaNamespace): + simple_type_name = schema_util.StripSchemaNamespace(type_name) + type_ = namespace.types[simple_type_name] + # Add more ways to forward declare things as necessary. + if type_.property_type == PropertyType.OBJECT: + c.Append('struct %s;' % simple_type_name) c.Append('}') c.Concat(self.GetNamespaceStart()) for (name, type_) in self._namespace.types.items(): - if not type_.functions and type_.type_ == PropertyType.OBJECT: + if not type_.functions and type_.property_type == PropertyType.OBJECT: c.Append('struct %s;' % schema_util.StripSchemaNamespace(name)) c.Concat(self.GetNamespaceEnd()) return c @@ -244,37 +199,32 @@ class CppTypeGenerator(object): c.Append('#include "base/json/json_writer.h"') return c - def _ResolveTypeNamespace(self, ref_type): + def _ResolveTypeNamespace(self, qualified_name): """Resolves a type, which must be explicitly qualified, to its enclosing namespace. """ - if ref_type in self._type_namespaces: - return self._type_namespaces[ref_type] - raise KeyError('Cannot resolve type: %s. Maybe it needs a namespace prefix ' - 'if it comes from another namespace?' % ref_type) - return None + namespace = self._type_namespaces.get(qualified_name, None) + if namespace is None: + raise KeyError('Cannot resolve type %s. Maybe it needs a prefix ' + 'if it comes from another namespace?' % qualified_type) + return namespace - def GetReferencedProperty(self, prop): - """Returns the property a property of type REF is referring to. + def FollowRef(self, type_): + """Follows $ref link of types to resolve the concrete type a ref refers to. If the property passed in is not of type PropertyType.REF, it will be returned unchanged. """ - if prop.type_ != PropertyType.REF: - return prop - return self._ResolveTypeNamespace(prop.ref_type).types.get(prop.ref_type, - None) + if not type_.property_type == PropertyType.REF: + return type_ + ref = type_.ref_type - def IsEnumOrEnumRef(self, prop): - """Returns true if the property is an ENUM or a reference to an ENUM. - """ - return self.GetReferencedProperty(prop).type_ == PropertyType.ENUM + without_namespace = ref + if '.' in ref: + without_namespace = ref.split('.', 1)[1] - def IsEnumRef(self, prop): - """Returns true if the property is a reference to an ENUM. - """ - return (prop.type_ == PropertyType.REF and - self.GetReferencedProperty(prop).type_ == PropertyType.ENUM) + # TODO(kalman): Do we need to keep on resolving? + return self._ResolveTypeNamespace(ref).types[without_namespace] def _NamespaceTypeDependencies(self): """Returns a dict containing a mapping of model.Namespace to the C++ type @@ -283,16 +233,16 @@ class CppTypeGenerator(object): dependencies = set() for function in self._namespace.functions.values(): for param in function.params: - dependencies |= self._PropertyTypeDependencies(param) + dependencies |= self._TypeDependencies(param.type_) if function.callback: for param in function.callback.params: - dependencies |= self._PropertyTypeDependencies(param) + dependencies |= self._TypeDependencies(param.type_) for type_ in self._namespace.types.values(): for prop in type_.properties.values(): - dependencies |= self._PropertyTypeDependencies(prop) + dependencies |= self._TypeDependencies(prop.type_) for event in self._namespace.events.values(): for param in event.params: - dependencies |= self._PropertyTypeDependencies(param) + dependencies |= self._TypeDependencies(param.type_) dependency_namespaces = dict() for dependency in dependencies: @@ -302,18 +252,17 @@ class CppTypeGenerator(object): dependency_namespaces[namespace].append(dependency) return dependency_namespaces - def _PropertyTypeDependencies(self, prop): + def _TypeDependencies(self, type_): """Recursively gets all the type dependencies of a property. """ deps = set() - if prop: - if prop.type_ == PropertyType.REF: - deps.add(prop.ref_type) - elif prop.type_ == PropertyType.ARRAY: - deps = self._PropertyTypeDependencies(prop.item_type) - elif prop.type_ == PropertyType.OBJECT: - for p in prop.properties.values(): - deps |= self._PropertyTypeDependencies(p) + if type_.property_type == PropertyType.REF: + deps.add(type_.ref_type) + elif type_.property_type == PropertyType.ARRAY: + deps = self._TypeDependencies(type_.item_type) + elif type_.property_type == PropertyType.OBJECT: + for p in type_.properties.values(): + deps |= self._TypeDependencies(p.type_) return deps def GeneratePropertyValues(self, property, line, nodoc=False): @@ -323,20 +272,19 @@ class CppTypeGenerator(object): if not nodoc: c.Comment(property.description) - if property.has_value: + if property.value is not None: c.Append(line % { - "type": self._GetPrimitiveType(property.type_), + "type": self.GetCppType(property.type_), "name": property.name, "value": property.value }) else: has_child_code = False c.Sblock('namespace %s {' % property.name) - for child_property in property.properties.values(): - child_code = self.GeneratePropertyValues( - child_property, - line, - nodoc=nodoc) + for child_property in property.type_.properties.values(): + child_code = self.GeneratePropertyValues(child_property, + line, + nodoc=nodoc) if child_code: has_child_code = True c.Concat(child_code) @@ -344,16 +292,3 @@ class CppTypeGenerator(object): if not has_child_code: c = None return c - - def _GetPrimitiveType(self, type_): - """Like |GetType| but only accepts and returns C++ primitive types. - """ - if type_ == PropertyType.BOOLEAN: - return 'bool' - elif type_ == PropertyType.INTEGER: - return 'int' - elif type_ == PropertyType.DOUBLE: - return 'double' - elif type_ == PropertyType.STRING: - return 'char*' - raise Exception(type_ + ' is not primitive') diff --git a/tools/json_schema_compiler/cpp_type_generator_test.py b/tools/json_schema_compiler/cpp_type_generator_test.py index 4bfa507..2317a02 100755 --- a/tools/json_schema_compiler/cpp_type_generator_test.py +++ b/tools/json_schema_compiler/cpp_type_generator_test.py @@ -71,33 +71,6 @@ class CppTypeGeneratorTest(unittest.TestCase): self.assertEquals('#include "base/string_number_conversions.h"', manager.GenerateIncludes().Render()) - - def testGenerateIncludesAndForwardDeclarationsMultipleTypes(self): - m = model.Model() - self.tabs_json[0]['types'].append(self.permissions_json[0]['types'][0]) - self.windows_json[0]['functions'].append( - self.permissions_json[0]['functions'][1]) - # Insert 'windows' before 'tabs' in order to test that they are sorted - # properly. - windows = m.AddNamespace(self.windows_json[0], - 'path/to/windows.json') - tabs_namespace = m.AddNamespace(self.tabs_json[0], - 'path/to/tabs.json') - manager = CppTypeGenerator('', windows, self.windows.unix_name) - manager.AddNamespace(tabs_namespace, self.tabs.unix_name) - self.assertEquals('#include "path/to/tabs.h"\n' - '#include "base/string_number_conversions.h"\n' - '#include "base/json/json_writer.h"', - manager.GenerateIncludes().Render()) - self.assertEquals('namespace tabs {\n' - 'struct Permissions;\n' - 'struct Tab;\n' - '}\n' - 'namespace windows {\n' - 'struct Window;\n' - '} // windows', - manager.GenerateForwardDeclarations().Render()) - def testGenerateIncludesAndForwardDeclarationsDependencies(self): m = model.Model() # Insert 'font_settings' before 'browser_action' in order to test that @@ -117,98 +90,79 @@ class CppTypeGeneratorTest(unittest.TestCase): '#include "base/string_number_conversions.h"', manager.GenerateIncludes().Render()) self.assertEquals('namespace browserAction {\n' - 'typedef std::vector<int> ColorArray;\n' '}\n' 'namespace fontSettings {\n' - 'typedef std::string FakeStringType;\n' '}\n' 'namespace dependency_tester {\n' '} // dependency_tester', manager.GenerateForwardDeclarations().Render()) - def testChoicesEnum(self): - manager = CppTypeGenerator('', self.tabs, self.tabs.unix_name) - prop = self.tabs.functions['move'].params[0] - self.assertEquals('TAB_IDS_ARRAY', - manager.GetEnumValue(prop, model.PropertyType.ARRAY.name)) - self.assertEquals( - 'TAB_IDS_INTEGER', - manager.GetEnumValue(prop, model.PropertyType.INTEGER.name)) - self.assertEquals('TabIdsType', - manager.GetChoicesEnumType(prop)) - - def testGetTypeSimple(self): + def testGetCppTypeSimple(self): manager = CppTypeGenerator('', self.tabs, self.tabs.unix_name) self.assertEquals( 'int', - manager.GetType(self.tabs.types['tabs.Tab'].properties['id'])) + manager.GetCppType(self.tabs.types['Tab'].properties['id'].type_)) self.assertEquals( 'std::string', - manager.GetType(self.tabs.types['tabs.Tab'].properties['status'])) + manager.GetCppType(self.tabs.types['Tab'].properties['status'].type_)) self.assertEquals( 'bool', - manager.GetType(self.tabs.types['tabs.Tab'].properties['selected'])) + manager.GetCppType(self.tabs.types['Tab'].properties['selected'].type_)) def testStringAsType(self): manager = CppTypeGenerator('', self.font_settings, self.font_settings.unix_name) self.assertEquals( 'std::string', - manager.GetType( - self.font_settings.types['fontSettings.FakeStringType'])) + manager.GetCppType(self.font_settings.types['FakeStringType'])) def testArrayAsType(self): manager = CppTypeGenerator('', self.browser_action, self.browser_action.unix_name) self.assertEquals( 'std::vector<int>', - manager.GetType(self.browser_action.types['browserAction.ColorArray'])) + manager.GetCppType(self.browser_action.types['ColorArray'])) - def testGetTypeArray(self): + def testGetCppTypeArray(self): manager = CppTypeGenerator('', self.windows, self.windows.unix_name) self.assertEquals( 'std::vector<linked_ptr<Window> >', - manager.GetType(self.windows.functions['getAll'].callback.params[0])) + manager.GetCppType( + self.windows.functions['getAll'].callback.params[0].type_)) manager = CppTypeGenerator('', self.permissions, self.permissions.unix_name) - self.assertEquals('std::vector<std::string>', manager.GetType( - self.permissions.types['permissions.Permissions'].properties['origins'])) + self.assertEquals( + 'std::vector<std::string>', + manager.GetCppType( + self.permissions.types['Permissions'].properties['origins'].type_)) - def testGetTypeLocalRef(self): + def testGetCppTypeLocalRef(self): manager = CppTypeGenerator('', self.tabs, self.tabs.unix_name) self.assertEquals( 'Tab', - manager.GetType(self.tabs.functions['get'].callback.params[0])) + manager.GetCppType(self.tabs.functions['get'].callback.params[0].type_)) - def testGetTypeIncludedRef(self): + def testGetCppTypeIncludedRef(self): manager = CppTypeGenerator('', self.windows, self.windows.unix_name) manager.AddNamespace(self.tabs, self.tabs.unix_name) self.assertEquals( 'std::vector<linked_ptr<tabs::Tab> >', - manager.GetType( - self.windows.types['windows.Window'].properties['tabs'])) + manager.GetCppType( + self.windows.types['Window'].properties['tabs'].type_)) - def testGetTypeNotfound(self): - prop = self.windows.types['windows.Window'].properties['tabs'].item_type - prop.ref_type = 'Something' - manager = CppTypeGenerator('', self.windows, self.windows.unix_name) - self.assertRaises(KeyError, manager.GetType, prop) - - def testGetTypeNotimplemented(self): - prop = self.windows.types['windows.Window'].properties['tabs'].item_type - prop.type_ = 10 - manager = CppTypeGenerator('', self.windows, self.windows.unix_name) - self.assertRaises(NotImplementedError, manager.GetType, prop) - - def testGetTypeWithPadForGeneric(self): + def testGetCppTypeWithPadForGeneric(self): manager = CppTypeGenerator('', self.permissions, self.permissions.unix_name) - self.assertEquals('std::vector<std::string> ', - manager.GetType( - self.permissions.types['permissions.Permissions'].properties['origins'], - pad_for_generics=True)) + self.assertEquals('std::vector<std::string>', + manager.GetCppType( + self.permissions.types['Permissions'].properties['origins'].type_, + is_in_container=False)) + self.assertEquals('linked_ptr<std::vector<std::string> >', + manager.GetCppType( + self.permissions.types['Permissions'].properties['origins'].type_, + is_in_container=True)) self.assertEquals('bool', - manager.GetType( - self.permissions.functions['contains'].callback.params[0], - pad_for_generics=True)) + manager.GetCppType( + self.permissions.functions['contains'].callback.params[0].type_, + is_in_container=True)) def testNamespaceDeclaration(self): manager = CppTypeGenerator('extensions', self.permissions, @@ -231,33 +185,5 @@ class CppTypeGeneratorTest(unittest.TestCase): '} // extensions', manager.GetRootNamespaceEnd().Render()) - def testExpandParams(self): - manager = CppTypeGenerator('extensions', self.tabs, - self.tabs.unix_name) - props = self.tabs.functions['move'].params - self.assertEquals(2, len(props)) - self.assertEquals(['move_properties', 'tab_ids_array', 'tab_ids_integer'], - sorted([x.unix_name for x in manager.ExpandParams(props)]) - ) - - def testGetAllPossibleParameterLists(self): - manager = CppTypeGenerator('extensions', self.tabs, - self.tabs.unix_name) - props = self.forbidden.functions['forbiddenParameters'].params - self.assertEquals(4, len(props)) - param_lists = manager.GetAllPossibleParameterLists(props) - expected_lists = [ - ['first_choice_array', 'first_string', - 'second_choice_array', 'second_string'], - ['first_choice_array', 'first_string', - 'second_choice_integer', 'second_string'], - ['first_choice_integer', 'first_string', - 'second_choice_array', 'second_string'], - ['first_choice_integer', 'first_string', - 'second_choice_integer', 'second_string']] - result_lists = sorted([[param.unix_name for param in param_list] - for param_list in param_lists]) - self.assertEquals(expected_lists, result_lists) - if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py index 96a5cd3..9ce14ca 100644 --- a/tools/json_schema_compiler/cpp_util.py +++ b/tools/json_schema_compiler/cpp_util.py @@ -1,11 +1,12 @@ # Copyright (c) 2012 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. + """Utilies and constants specific to Chromium C++ code. """ from datetime import datetime -from model import PropertyType +from model import Property, PropertyType, Type import os CHROMIUM_LICENSE = ( @@ -31,7 +32,7 @@ def Classname(s): """ return '_'.join([x[0].upper() + x[1:] for x in s.split('.')]) -def GetAsFundamentalValue(prop, src, dst): +def GetAsFundamentalValue(type_, src, dst): """Returns the C++ code for retrieving a fundamental type from a Value into a variable. @@ -43,7 +44,7 @@ def GetAsFundamentalValue(prop, src, dst): PropertyType.BOOLEAN: '%s->GetAsBoolean(%s)', PropertyType.INTEGER: '%s->GetAsInteger(%s)', PropertyType.DOUBLE: '%s->GetAsDouble(%s)', - }[prop.type_] % (src, dst) + }[type_.property_type] % (src, dst) def GetValueType(type_): """Returns the Value::Type corresponding to the model.PropertyType. @@ -58,15 +59,18 @@ def GetValueType(type_): PropertyType.FUNCTION: 'Value::TYPE_DICTIONARY', PropertyType.ARRAY: 'Value::TYPE_LIST', PropertyType.BINARY: 'Value::TYPE_BINARY', - }[type_] + }[type_.property_type] def GetParameterDeclaration(param, type_): """Gets a parameter declaration of a given model.Property and its C++ type. """ - if param.type_ in (PropertyType.REF, PropertyType.OBJECT, PropertyType.ARRAY, - PropertyType.STRING, PropertyType.ANY): - arg = '%(type)s& %(name)s' + if param.type_.property_type in (PropertyType.REF, + PropertyType.OBJECT, + PropertyType.ARRAY, + PropertyType.STRING, + PropertyType.ANY): + arg = 'const %(type)s& %(name)s' else: arg = '%(type)s %(name)s' return arg % { @@ -82,40 +86,8 @@ def GenerateIfndefName(path, filename): return (('%s_%s_H__' % (path, filename)) .upper().replace(os.sep, '_').replace('/', '_')) -def GenerateTypeToCompiledTypeConversion(prop, from_, to): - try: - return _GenerateTypeConversionHelper(prop.type_, prop.compiled_type, from_, - to) - except KeyError: - raise NotImplementedError('Conversion from %s to %s in %s not supported' % - (prop.type_, prop.compiled_type, prop.name)) - -def GenerateCompiledTypeToTypeConversion(prop, from_, to): - try: - return _GenerateTypeConversionHelper(prop.compiled_type, prop.type_, from_, - to) - except KeyError: - raise NotImplementedError('Conversion from %s to %s in %s not supported' % - (prop.compiled_type, prop.type_, prop.name)) - -def _GenerateTypeConversionHelper(from_type, to_type, from_, to): - """Converts from PropertyType from_type to PropertyType to_type. - - from_type: The PropertyType to be converted from. - to_type: The PropertyType to be converted to. - from_: The variable name of the type to be converted from. - to: The variable name of the type to be converted to. +def PadForGenerics(var): + """Appends a space to |var| if it ends with a >, so that it can be compiled + within generic types. """ - # TODO(mwrosen): Add support for more from/to combinations as necessary. - return { - PropertyType.STRING: { - PropertyType.INTEGER: 'base::StringToInt(%(from)s, &%(to)s)', - PropertyType.INT64: 'base::StringToInt64(%(from)s, &%(to)s)', - }, - PropertyType.INTEGER: { - PropertyType.STRING: '%(to)s = base::IntToString(%(from)s)', - }, - PropertyType.INT64: { - PropertyType.STRING: '%(to)s = base::Int64ToString(%(from)s)', - } - }[from_type][to_type] % {'from': from_, 'to': to} + return ('%s ' % var) if var.endswith('>') else var diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py index 05a0241..0bc44b7 100644 --- a/tools/json_schema_compiler/h_generator.py +++ b/tools/json_schema_compiler/h_generator.py @@ -3,7 +3,7 @@ # found in the LICENSE file. from code import Code -from model import PropertyType +from model import PropertyType, Type import cpp_util import schema_util @@ -11,10 +11,10 @@ class HGenerator(object): """A .h generator for a namespace. """ def __init__(self, namespace, cpp_type_generator): - self._cpp_type_generator = cpp_type_generator + self._type_helper = cpp_type_generator self._namespace = namespace self._target_namespace = ( - self._cpp_type_generator.GetCppNamespaceName(self._namespace)) + self._type_helper.GetCppNamespaceName(self._namespace)) def Generate(self): """Generates a Code object with the .h for a single namespace. @@ -31,6 +31,7 @@ class HGenerator(object): (c.Append('#ifndef %s' % ifndef_name) .Append('#define %s' % ifndef_name) .Append() + .Append('#include <map>') .Append('#include <string>') .Append('#include <vector>') .Append() @@ -39,24 +40,22 @@ class HGenerator(object): .Append('#include "base/memory/linked_ptr.h"') .Append('#include "base/memory/scoped_ptr.h"') .Append('#include "base/values.h"') - .Append('#include "tools/json_schema_compiler/any.h"') .Append() ) - c.Concat(self._cpp_type_generator.GetRootNamespaceStart()) + c.Concat(self._type_helper.GetRootNamespaceStart()) # TODO(calamity): These forward declarations should be #includes to allow # $ref types from other files to be used as required params. This requires # some detangling of windows and tabs which will currently lead to circular # #includes. forward_declarations = ( - self._cpp_type_generator.GenerateForwardDeclarations()) + self._type_helper.GenerateForwardDeclarations()) if not forward_declarations.IsEmpty(): (c.Append() - .Concat(forward_declarations) - .Append() + .Cblock(forward_declarations) ) - c.Concat(self._cpp_type_generator.GetNamespaceStart()) + c.Concat(self._type_helper.GetNamespaceStart()) c.Append() if self._namespace.properties: (c.Append('//') @@ -65,21 +64,20 @@ class HGenerator(object): .Append() ) for property in self._namespace.properties.values(): - property_code = self._cpp_type_generator.GeneratePropertyValues( + property_code = self._type_helper.GeneratePropertyValues( property, 'extern const %(type)s %(name)s;') if property_code: - c.Concat(property_code).Append() + c.Cblock(property_code) if self._namespace.types: (c.Append('//') .Append('// Types') .Append('//') .Append() + .Cblock(self._GenerateTypes(self._FieldDependencyOrder(), + is_toplevel=True, + generate_typedefs=True)) ) - for type_ in self._FieldDependencyOrder(): - (c.Concat(self._GenerateType(type_)) - .Append() - ) if self._namespace.functions: (c.Append('//') .Append('// Functions') @@ -87,9 +85,7 @@ class HGenerator(object): .Append() ) for function in self._namespace.functions.values(): - (c.Concat(self._GenerateFunction(function)) - .Append() - ) + c.Cblock(self._GenerateFunction(function)) if self._namespace.events: (c.Append('//') .Append('// Events') @@ -97,12 +93,9 @@ class HGenerator(object): .Append() ) for event in self._namespace.events.values(): - (c.Concat(self._GenerateEvent(event)) - .Append() - ) - (c.Concat(self._cpp_type_generator.GetNamespaceEnd()) - .Concat(self._cpp_type_generator.GetRootNamespaceEnd()) - .Append() + c.Cblock(self._GenerateEvent(event)) + (c.Concat(self._type_helper.GetNamespaceEnd()) + .Cblock(self._type_helper.GetRootNamespaceEnd()) .Append('#endif // %s' % ifndef_name) .Append() ) @@ -129,113 +122,158 @@ class HGenerator(object): ExpandType([], type_) return dependency_order - def _GenerateEnumDeclaration(self, enum_name, prop, values): - """Generate the declaration of a C++ enum for the given property and - values. + def _GenerateEnumDeclaration(self, enum_name, type_): + """Generate the declaration of a C++ enum. """ c = Code() c.Sblock('enum %s {' % enum_name) - c.Append(self._cpp_type_generator.GetEnumNoneValue(prop) + ',') - for value in values: - c.Append(self._cpp_type_generator.GetEnumValue(prop, value) + ',') - (c.Eblock('};') - .Append() - ) - return c + c.Append(self._type_helper.GetEnumNoneValue(type_) + ',') + for value in type_.enum_values: + c.Append(self._type_helper.GetEnumValue(type_, value) + ',') + return c.Eblock('};') def _GenerateFields(self, props): """Generates the field declarations when declaring a type. """ c = Code() - # Generate the enums needed for any fields with "choices" + needs_blank_line = False for prop in props: - if prop.type_ == PropertyType.CHOICES: - enum_name = self._cpp_type_generator.GetChoicesEnumType(prop) - c.Append('%s %s_type;' % (enum_name, prop.unix_name)) + if needs_blank_line: c.Append() - - for prop in self._cpp_type_generator.ExpandParams(props): + needs_blank_line = True if prop.description: c.Comment(prop.description) + # ANY is a base::Value which is abstract and cannot be a direct member, so + # we always need to wrap it in a scoped_ptr. + is_ptr = prop.optional or prop.type_.property_type == PropertyType.ANY (c.Append('%s %s;' % ( - self._cpp_type_generator.GetCompiledType(prop, wrap_optional=True), + self._type_helper.GetCppType(prop.type_, is_ptr=is_ptr), prop.unix_name)) - .Append() ) return c - def _GenerateType(self, type_): - """Generates a struct for a type. + def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False): + """Generates a struct for |type_|. + + |is_toplevel| implies that the type was declared in the "types" field + of an API schema. This determines the correct function + modifier(s). + |generate_typedefs| controls whether primitive types should be generated as + a typedef. This may not always be desired. If false, + primitive types are ignored. """ classname = cpp_util.Classname(schema_util.StripSchemaNamespace(type_.name)) c = Code() if type_.functions: - c.Sblock('namespace %(classname)s {') + # Wrap functions within types in the type's namespace. + (c.Append('namespace %s {' % classname) + .Append() + ) for function in type_.functions.values(): - (c.Concat(self._GenerateFunction(function)) - .Append() - ) - c.Eblock('}') - elif type_.type_ == PropertyType.ARRAY: - if type_.description: + c.Cblock(self._GenerateFunction(function)) + c.Append('} // namespace %s' % classname) + elif type_.property_type == PropertyType.ARRAY: + if generate_typedefs and type_.description: c.Comment(type_.description) - c.Append('typedef std::vector<%(item_type)s> %(classname)s;') - c.Substitute({'classname': classname, 'item_type': - self._cpp_type_generator.GetCompiledType(type_.item_type, - wrap_optional=True)}) - elif type_.type_ == PropertyType.STRING: - if type_.description: - c.Comment(type_.description) - c.Append('typedef std::string %(classname)s;') - elif type_.type_ == PropertyType.ENUM: + c.Cblock(self._GenerateType(type_.item_type)) + if generate_typedefs: + (c.Append('typedef std::vector<%s > %s;' % ( + self._type_helper.GetCppType(type_.item_type), + classname)) + ) + elif type_.property_type == PropertyType.STRING: + if generate_typedefs: + if type_.description: + c.Comment(type_.description) + c.Append('typedef std::string %(classname)s;') + elif type_.property_type == PropertyType.ENUM: if type_.description: c.Comment(type_.description) c.Sblock('enum %(classname)s {') - c.Append('%s,' % self._cpp_type_generator.GetEnumNoneValue(type_)) + c.Append('%s,' % self._type_helper.GetEnumNoneValue(type_)) for value in type_.enum_values: - c.Append('%s,' % self._cpp_type_generator.GetEnumValue(type_, value)) + c.Append('%s,' % self._type_helper.GetEnumValue(type_, value)) + # Top level enums are in a namespace scope so the methods shouldn't be + # static. On the other hand, those declared inline (e.g. in an object) do. + maybe_static = '' if is_toplevel else 'static ' (c.Eblock('};') .Append() - .Append('scoped_ptr<base::Value> CreateEnumValue(%s %s);' % - (classname, classname.lower())) - .Append('std::string ToString(%s enum_param);' % classname) - .Append('%s From%sString(const std::string& enum_string);' % - (classname, classname)) + .Append('%sstd::string ToString(%s as_enum);' % + (maybe_static, classname)) + .Append('%s%s Parse%s(const std::string& as_string);' % + (maybe_static, classname, classname)) ) - else: + elif type_.property_type == PropertyType.OBJECT: if type_.description: c.Comment(type_.description) (c.Sblock('struct %(classname)s {') - .Append('~%(classname)s();') .Append('%(classname)s();') - .Append() - .Concat(self._GeneratePropertyStructures(type_.properties.values())) - .Concat(self._GenerateFields(type_.properties.values())) + .Append('~%(classname)s();') ) - if type_.from_json: - (c.Comment('Populates a %s object from a base::Value. Returns' + if type_.origin.from_json: + (c.Append() + .Comment('Populates a %s object from a base::Value. Returns' ' whether |out| was successfully populated.' % classname) .Append('static bool Populate(const base::Value& value, ' '%(classname)s* out);') - .Append() ) - - if type_.from_client: - (c.Comment('Returns a new base::DictionaryValue representing the' - ' serialized form of this %s object. Passes ' - 'ownership to caller.' % classname) + if type_.origin.from_client: + (c.Append() + .Comment('Returns a new base::DictionaryValue representing the' + ' serialized form of this %s object.' % classname) .Append('scoped_ptr<base::DictionaryValue> ToValue() const;') ) - + properties = type_.properties.values() + (c.Append() + .Cblock(self._GenerateTypes(p.type_ for p in properties)) + .Cblock(self._GenerateFields(properties))) + if type_.additional_properties is not None: + # Most additionalProperties actually have type "any", which is better + # modelled as a DictionaryValue rather than a map of string -> Value. + if type_.additional_properties.property_type == PropertyType.ANY: + c.Append('base::DictionaryValue additional_properties;') + else: + (c.Cblock(self._GenerateType(type_.additional_properties)) + .Append('std::map<std::string, %s> additional_properties;' % + cpp_util.PadForGenerics( + self._type_helper.GetCppType(type_.additional_properties, + is_in_container=True))) + ) (c.Eblock() .Sblock(' private:') - .Concat(self._GeneratePrivatePropertyStructures( - type_.properties.values())) - .Append() .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);') .Eblock('};') ) + elif type_.property_type == PropertyType.CHOICES: + if type_.description: + c.Comment(type_.description) + # Choices are modelled with optional fields for each choice. Exactly one + # field of the choice is guaranteed to be set by the compiler. + (c.Sblock('struct %(classname)s {') + .Append('%(classname)s();') + .Append('~%(classname)s();') + .Append()) + c.Cblock(self._GenerateTypes(type_.choices)) + if type_.origin.from_json: + (c.Comment('Populates a %s object from a base::Value. Returns' + ' whether |out| was successfully populated.' % classname) + .Append('static bool Populate(const base::Value& value, ' + '%(classname)s* out);') + .Append() + ) + if type_.origin.from_client: + (c.Comment('Returns a new base::Value representing the' + ' serialized form of this %s object.' % classname) + .Append('scoped_ptr<base::Value> ToValue() const;') + .Append() + ) + c.Append('// Choices:') + for choice_type in type_.choices: + c.Append('%s as_%s;' % ( + self._type_helper.GetCppType(choice_type, is_ptr=True), + choice_type.unix_name)) + c.Eblock('};') c.Substitute({'classname': classname}) return c @@ -243,10 +281,12 @@ class HGenerator(object): """Generates the namespaces for an event. """ c = Code() - (c.Sblock('namespace %s {' % cpp_util.Classname(event.name)) - .Concat(self._GenerateCreateCallbackArguments(event, - generate_to_json=True)) - .Eblock('};') + # TODO(kalman): use event.unix_name not Classname. + event_namespace = cpp_util.Classname(event.name) + (c.Append('namespace %s {' % event_namespace) + .Append() + .Concat(self._GenerateCreateCallbackArguments(event)) + .Eblock('} // namespace %s' % event_namespace) ) return c @@ -254,125 +294,74 @@ class HGenerator(object): """Generates the namespaces and structs for a function. """ c = Code() - (c.Sblock('namespace %s {' % cpp_util.Classname(function.name)) - .Concat(self._GenerateFunctionParams(function)) - .Append() + # TODO(kalman): Use function.unix_name not Classname here. + function_namespace = cpp_util.Classname(function.name) + (c.Append('namespace %s {' % function_namespace) + .Append() + .Cblock(self._GenerateFunctionParams(function)) ) if function.callback: - (c.Concat(self._GenerateFunctionResults(function.callback)) - .Append() - ) - c.Eblock('};') - + c.Cblock(self._GenerateFunctionResults(function.callback)) + c.Append('} // namespace %s' % function_namespace) return c def _GenerateFunctionParams(self, function): """Generates the struct for passing parameters from JSON to a function. """ - c = Code() - - if function.params: - (c.Sblock('struct Params {') - .Concat(self._GeneratePropertyStructures(function.params)) - .Concat(self._GenerateFields(function.params)) - .Append('~Params();') - .Append() - .Append('static scoped_ptr<Params> Create(' - 'const base::ListValue& args);') - .Eblock() - .Sblock(' private:') - .Append('Params();') - .Append() - .Append('DISALLOW_COPY_AND_ASSIGN(Params);') - .Eblock('};') - ) + if not function.params: + return Code() + c = Code() + (c.Sblock('struct Params {') + .Append('static scoped_ptr<Params> Create(const base::ListValue& args);') + .Append('~Params();') + .Append() + .Cblock(self._GenerateTypes(p.type_ for p in function.params)) + .Cblock(self._GenerateFields(function.params)) + .Eblock() + .Sblock(' private:') + .Append('Params();') + .Append() + .Append('DISALLOW_COPY_AND_ASSIGN(Params);') + .Eblock('};') + ) return c - def _GeneratePropertyStructures(self, props): + def _GenerateTypes(self, types, is_toplevel=False, generate_typedefs=False): """Generate the structures required by a property such as OBJECT classes and enums. """ c = Code() - for prop in props: - if prop.type_ == PropertyType.OBJECT: - c.Concat(self._GenerateType(prop)) - c.Append() - elif prop.type_ == PropertyType.ARRAY: - c.Concat(self._GeneratePropertyStructures([prop.item_type])) - c.Append() - elif prop.type_ == PropertyType.CHOICES: - c.Concat(self._GenerateEnumDeclaration( - self._cpp_type_generator.GetChoicesEnumType(prop), - prop, - [choice.type_.name for choice in prop.choices.values()])) - c.Concat(self._GeneratePropertyStructures(prop.choices.values())) - elif prop.type_ == PropertyType.ENUM: - enum_name = self._cpp_type_generator.GetCompiledType(prop) - c.Concat(self._GenerateEnumDeclaration( - enum_name, - prop, - prop.enum_values)) - create_enum_value = ('scoped_ptr<base::Value> CreateEnumValue(%s %s);' % - (enum_name, prop.unix_name)) - enum_to_string = 'std::string ToString(%s enum_param);' % enum_name - enum_from_string = ('%s From%sString(const std::string& enum_string);' % - (enum_name, enum_name)) - # If the property is from the UI then we're in a struct so this function - # should be static. If it's from the client, then we're just in a - # namespace so we can't have the static keyword. - if prop.from_json: - create_enum_value = 'static %s' % create_enum_value - enum_to_string = 'static %s' % enum_to_string - enum_from_string = 'static %s' % enum_from_string - (c.Append(create_enum_value) - .Append(enum_to_string) - .Append(enum_from_string)) - return c - - def _GeneratePrivatePropertyStructures(self, props): - """Generate the private structures required by a property such as OBJECT - classes and enums. - """ - c = Code() - for prop in props: - if prop.type_ == PropertyType.ARRAY: - c.Concat(self._GeneratePrivatePropertyStructures([prop.item_type])) - c.Append() - elif prop.type_ == PropertyType.CHOICES: - # We only need GetChoiceValue() if there is a ToValue() method. - if prop.from_client: - c.Append('scoped_ptr<base::Value> Get%sChoiceValue() const;' % ( - cpp_util.Classname(prop.name))) + for type_ in types: + c.Cblock(self._GenerateType(type_, + is_toplevel=is_toplevel, + generate_typedefs=generate_typedefs)) return c - def _GenerateCreateCallbackArguments(self, function, generate_to_json=False): - """Generates functions for passing paramaters to a callback. + def _GenerateCreateCallbackArguments(self, function): + """Generates functions for passing parameters to a callback. """ c = Code() params = function.params - c.Concat(self._GeneratePropertyStructures(params)) + c.Cblock(self._GenerateTypes((p.type_ for p in params), is_toplevel=True)) - param_lists = self._cpp_type_generator.GetAllPossibleParameterLists(params) - for param_list in param_lists: - declaration_list = [] - for param in param_list: - if param.description: - c.Comment(param.description) - declaration_list.append('const %s' % cpp_util.GetParameterDeclaration( - param, self._cpp_type_generator.GetCompiledType(param))) - c.Append('scoped_ptr<base::ListValue> Create(%s);' % - ', '.join(declaration_list)) - if generate_to_json: - c.Append('std::string ToJson(%s);' % ', '.join(declaration_list)) + declaration_list = [] + for param in params: + if param.description: + c.Comment(param.description) + declaration_list.append(cpp_util.GetParameterDeclaration( + param, self._type_helper.GetCppType(param.type_))) + c.Append('scoped_ptr<base::ListValue> Create(%s);' % + ', '.join(declaration_list)) return c def _GenerateFunctionResults(self, callback): """Generates namespace for passing a function's result back. """ c = Code() - (c.Sblock('namespace Results {') - .Concat(self._GenerateCreateCallbackArguments(callback)) - .Eblock('};') + (c.Append('namespace Results {') + .Append() + .Concat(self._GenerateCreateCallbackArguments(callback)) + .Append('} // namespace Results') ) return c diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py index 30dad11..907aacf 100644 --- a/tools/json_schema_compiler/idl_schema.py +++ b/tools/json_schema_compiler/idl_schema.py @@ -352,7 +352,6 @@ class IDLSchema(object): continue else: sys.exit('Did not process %s %s' % (node.cls, node)) - schema_util.PrefixSchemasWithNamespace(namespaces) return namespaces def Load(filename): diff --git a/tools/json_schema_compiler/idl_schema_test.py b/tools/json_schema_compiler/idl_schema_test.py index a4fd0d8..873333b 100755 --- a/tools/json_schema_compiler/idl_schema_test.py +++ b/tools/json_schema_compiler/idl_schema_test.py @@ -38,14 +38,14 @@ class IdlSchemaTest(unittest.TestCase): self.assertEquals(expected, getParams(schema, 'function5')) expected = [{'type':'function', 'name':'cb', - 'parameters':[{'name':'arg', '$ref':'idl_basics.MyType1'}]}] + 'parameters':[{'name':'arg', '$ref':'MyType1'}]}] self.assertEquals(expected, getParams(schema, 'function6')) def testCallbackWithArrayArgument(self): schema = self.idl_basics expected = [{'type':'function', 'name':'cb', 'parameters':[{'name':'arg', 'type':'array', - 'items':{'$ref':'idl_basics.MyType2'}}]}] + 'items':{'$ref':'MyType2'}}]}] self.assertEquals(expected, getParams(schema, 'function12')) @@ -61,20 +61,20 @@ class IdlSchemaTest(unittest.TestCase): 'x': {'name': 'x', 'type': 'integer', 'enum': [1,2], 'description': 'This comment tests "double-quotes".'}, 'y': {'name': 'y', 'type': 'string'}}, - getType(self.idl_basics, 'idl_basics.MyType1')['properties']) + getType(self.idl_basics, 'MyType1')['properties']) def testEnum(self): schema = self.idl_basics expected = {'enum': ['name1', 'name2'], 'description': 'Enum description', - 'type': 'string', 'id': 'idl_basics.EnumType'} + 'type': 'string', 'id': 'EnumType'} self.assertEquals(expected, getType(schema, expected['id'])) - expected = [{'name':'type', '$ref':'idl_basics.EnumType'}, + expected = [{'name':'type', '$ref':'EnumType'}, {'type':'function', 'name':'cb', - 'parameters':[{'name':'type', '$ref':'idl_basics.EnumType'}]}] + 'parameters':[{'name':'type', '$ref':'EnumType'}]}] self.assertEquals(expected, getParams(schema, 'function13')) - expected = [{'items': {'$ref': 'idl_basics.EnumType'}, 'name': 'types', + expected = [{'items': {'$ref': 'EnumType'}, 'name': 'types', 'type': 'array'}] self.assertEquals(expected, getParams(schema, 'function14')) @@ -114,7 +114,7 @@ class IdlSchemaTest(unittest.TestCase): [{'description': ('So should this comment about the argument. ' '<em>HTML</em> is fine too.'), 'name': 'arg', - '$ref': 'idl_basics.MyType1'}], + '$ref': 'MyType1'}], func['parameters']) func = getFunction(schema, 'function4') self.assertEquals(('This tests if "double-quotes" are escaped correctly.' @@ -124,21 +124,21 @@ class IdlSchemaTest(unittest.TestCase): def testReservedWords(self): schema = idl_schema.Load('test/idl_reserved_words.idl')[0] - foo_type = getType(schema, 'reserved_words.Foo') + foo_type = getType(schema, 'Foo') self.assertEquals(['float', 'DOMString'], foo_type['enum']) - enum_type = getType(schema, 'reserved_words.enum') + enum_type = getType(schema, 'enum') self.assertEquals(['callback', 'namespace'], enum_type['enum']) - dictionary = getType(schema, 'reserved_words.dictionary'); + dictionary = getType(schema, 'dictionary'); self.assertEquals('integer', dictionary['properties']['long']['type']) - mytype = getType(schema, 'reserved_words.MyType') + mytype = getType(schema, 'MyType') self.assertEquals('string', mytype['properties']['interface']['type']) params = getParams(schema, 'static') - self.assertEquals('reserved_words.Foo', params[0]['$ref']) - self.assertEquals('reserved_words.enum', params[1]['$ref']) + self.assertEquals('Foo', params[0]['$ref']) + self.assertEquals('enum', params[1]['$ref']) if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/json_schema.py b/tools/json_schema_compiler/json_schema.py index 370bc9b..8ee53a8 100644 --- a/tools/json_schema_compiler/json_schema.py +++ b/tools/json_schema_compiler/json_schema.py @@ -31,7 +31,6 @@ def DeleteNocompileNodes(item): def Load(filename): with open(filename, 'r') as handle: schemas = json_parse.Parse(handle.read()) - schema_util.PrefixSchemasWithNamespace(schemas) return schemas # A dictionary mapping |filename| to the object resulting from loading the JSON diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index 36be5a3..63794279 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -60,76 +60,129 @@ class Namespace(object): self.source_file_dir, self.source_file_filename = os.path.split(source_file) self.parent = None self.platforms = _GetPlatforms(json) - _AddTypes(self, json, self) - _AddFunctions(self, json, self) - _AddEvents(self, json, self) - _AddProperties(self, json, self) + toplevel_origin = Origin(from_client=True, from_json=True) + self.types = _GetTypes(self, json, self, toplevel_origin) + self.functions = _GetFunctions(self, json, self) + self.events = _GetEvents(self, json, self) + self.properties = _GetProperties(self, json, self, toplevel_origin) if include_compiler_options: self.compiler_options = json.get('compiler_options', {}) +class Origin(object): + """Stores the possible origin of model object as a pair of bools. These are: + + |from_client| indicating that instances can originate from users of + generated code (for example, function results), or + |from_json| indicating that instances can originate from the JSON (for + example, function parameters) + + It is possible for model objects to originate from both the client and json, + for example Types defined in the top-level schema, in which case both + |from_client| and |from_json| would be True. + """ + def __init__(self, from_client=False, from_json=False): + if not from_client and not from_json: + raise ValueError('One of from_client or from_json must be true') + self.from_client = from_client + self.from_json = from_json + class Type(object): """A Type defined in the json. Properties: - |name| the type name + - |namespace| the Type's namespace - |description| the description of the type (if provided) - |properties| a map of property unix_names to their model.Property - |functions| a map of function names to their model.Function - |events| a map of event names to their model.Event - - |from_client| indicates that instances of the Type can originate from the - users of generated code, such as top-level types and function results - - |from_json| indicates that instances of the Type can originate from the - JSON (as described by the schema), such as top-level types and function - parameters - - |type_| the PropertyType of this Type + - |origin| the Origin of the type + - |property_type| the PropertyType of this Type - |item_type| if this is an array, the type of items in the array - |simple_name| the name of this Type without a namespace + - |additional_properties| the type of the additional properties, if any is + specified """ - def __init__(self, parent, name, json, namespace): - if json.get('type') == 'array': - self.type_ = PropertyType.ARRAY - self.item_type = Property(self, - name + "Element", - json['items'], - namespace, - from_json=True, - from_client=True) - elif 'enum' in json: - self.enum_values = [] - for value in json['enum']: - self.enum_values.append(value) - self.type_ = PropertyType.ENUM - elif json.get('type') == 'string': - self.type_ = PropertyType.STRING - else: + def __init__(self, + parent, + name, + json, + namespace, + origin): + self.name = name + self.namespace = namespace + self.simple_name = _StripNamespace(self.name, namespace) + self.unix_name = UnixName(self.name) + self.description = json.get('description', None) + self.origin = origin + self.parent = parent + self.instance_of = json.get('isInstanceOf', None) + + # TODO(kalman): Only objects need functions/events/properties, but callers + # assume that all types have them. Fix this. + self.functions = _GetFunctions(self, json, namespace) + self.events = _GetEvents(self, json, namespace) + self.properties = _GetProperties(self, json, namespace, origin) + + json_type = json.get('type', None) + if json_type == 'array': + self.property_type = PropertyType.ARRAY + self.item_type = Type( + self, '%sType' % name, json['items'], namespace, origin) + elif '$ref' in json: + self.property_type = PropertyType.REF + self.ref_type = json['$ref'] + elif 'enum' in json and json_type == 'string': + self.property_type = PropertyType.ENUM + self.enum_values = [value for value in json['enum']] + elif json_type == 'any': + self.property_type = PropertyType.ANY + elif json_type == 'binary': + self.property_type = PropertyType.BINARY + elif json_type == 'boolean': + self.property_type = PropertyType.BOOLEAN + elif json_type == 'integer': + self.property_type = PropertyType.INTEGER + elif (json_type == 'double' or + json_type == 'number'): + self.property_type = PropertyType.DOUBLE + elif json_type == 'string': + self.property_type = PropertyType.STRING + elif 'choices' in json: + self.property_type = PropertyType.CHOICES + self.choices = [Type(self, + # The name of the choice type - there had better be + # either a type or a $ref specified for the choice. + json.get('type', json.get('$ref')), + json, + namespace, + origin) + for json in json['choices']] + elif json_type == 'object': if not ( 'properties' in json or 'additionalProperties' in json or 'functions' in json or 'events' in json): raise ParseException(self, name + " has no properties or functions") - self.type_ = PropertyType.OBJECT - self.name = name - self.simple_name = _StripNamespace(self.name, namespace) - self.unix_name = UnixName(self.name) - self.description = json.get('description') - self.from_json = True - self.from_client = True - self.parent = parent - self.instance_of = json.get('isInstanceOf', None) - _AddFunctions(self, json, namespace) - _AddEvents(self, json, namespace) - _AddProperties(self, json, namespace, from_json=True, from_client=True) - - additional_properties_key = 'additionalProperties' - additional_properties = json.get(additional_properties_key) - if additional_properties: - self.properties[additional_properties_key] = Property( - self, - additional_properties_key, - additional_properties, - namespace, - is_additional_properties=True) + self.property_type = PropertyType.OBJECT + additional_properties_json = json.get('additionalProperties', None) + if additional_properties_json is not None: + self.additional_properties = Type(self, + 'additionalProperties', + additional_properties_json, + namespace, + origin) + else: + self.additional_properties = None + elif json_type == 'function': + self.property_type = PropertyType.FUNCTION + # Sometimes we might have an unnamed function, e.g. if it's a property + # of an object. Use the name of the property in that case. + function_name = json.get('name', name) + self.function = Function(self, function_name, json, namespace, origin) + else: + raise ParseException(self, 'Unsupported JSON type %s' % json_type) class Function(object): """A Function defined in the API. @@ -149,11 +202,11 @@ class Function(object): """ def __init__(self, parent, + name, json, namespace, - from_json=False, - from_client=False): - self.name = json['name'] + origin): + self.name = name self.simple_name = _StripNamespace(self.name, namespace) self.platforms = _GetPlatforms(json) self.params = [] @@ -167,18 +220,14 @@ class Function(object): self.actions = options.get('actions', []) self.supports_listeners = options.get('supportsListeners', True) self.supports_rules = options.get('supportsRules', False) + def GeneratePropertyFromParam(p): - return Property(self, - p['name'], p, - namespace, - from_json=from_json, - from_client=from_client) + return Property.FromJSON(self, p['name'], p, namespace, origin) self.filters = [GeneratePropertyFromParam(filter) for filter in json.get('filters', [])] callback_param = None for param in json.get('parameters', []): - if param.get('type') == 'function': if callback_param: # No ParseException because the webstore has this. @@ -190,153 +239,97 @@ class Function(object): if callback_param: self.callback = Function(self, + callback_param['name'], callback_param, namespace, - from_client=True) + Origin(from_client=True)) self.returns = None if 'returns' in json: - self.returns = Property(self, 'return', json['returns'], namespace) + self.returns = Property.FromJSON( + self, 'return', json['returns'], namespace, origin) class Property(object): """A property of a type OR a parameter to a function. - Properties: - |name| name of the property as in the json. This shouldn't change since it is the key used to access DictionaryValues - |unix_name| the unix_style_name of the property. Used as variable name - |optional| a boolean representing whether the property is optional - |description| a description of the property (if provided) - - |type_| the model.PropertyType of this property - - |compiled_type| the model.PropertyType that this property should be - compiled to from the JSON. Defaults to |type_|. - - |ref_type| the type that the REF property is referencing. Can be used to - map to its model.Type - - |item_type| a model.Property representing the type of each element in an - ARRAY - - |properties| the properties of an OBJECT parameter - - |from_client| indicates that instances of the Type can originate from the - users of generated code, such as top-level types and function results - - |from_json| indicates that instances of the Type can originate from the - JSON (as described by the schema), such as top-level types and function - parameters + - |type_| the model.Type of this property - |simple_name| the name of this Property without a namespace """ + @staticmethod + def FromJSON(parent, name, json, namespace, origin): + """Creates a Property from JSON. + """ + opt_args = {} + if 'description' in json: + opt_args['description'] = json['description'] + if 'optional' in json: + opt_args['optional'] = json.get('optional') + if 'isInstanceOf' in json: + opt_args['instance_of'] = json.get('isInstanceOf') + + # HACK: only support very specific value types. + is_allowed_value = ( + '$ref' not in json and + ('type' not in json or json['type'] == 'integer' + or json['type'] == 'string')) + + if 'value' in json and is_allowed_value: + value = json['value'] + opt_args['value'] = value + if 'type' not in json: + # Sometimes the type of the value is left out, and we need to figure + # it out for ourselves. + if isinstance(value, int): + json['type'] = 'integer' + elif isinstance(value, basestring): + json['type'] = 'string' + else: + # TODO(kalman): support more types as necessary. + raise ParseException( + parent, '"%s" is not a supported type for "value"' % type(value)) + + type_ = Type(parent, name, json, namespace, origin) + return Property(parent, + name, + namespace, + type_, + origin, + **opt_args); + def __init__(self, parent, name, - json, namespace, - is_additional_properties=False, - from_json=False, - from_client=False): + type_, + origin, + description=None, + optional=False, + returns=None, + instance_of=None, + value=None): + """Directly initializes the fields of the Property. + """ self.name = name self.simple_name = _StripNamespace(self.name, namespace) self._unix_name = UnixName(self.name) self._unix_name_used = False - self.optional = json.get('optional', False) - self.functions = OrderedDict() - self.has_value = False - self.description = json.get('description') + self.optional = optional + self.description = description self.parent = parent - self.from_json = from_json - self.from_client = from_client - self.instance_of = json.get('isInstanceOf', None) - self.params = [] - self.returns = None - _AddProperties(self, json, namespace) - if is_additional_properties: - self.type_ = PropertyType.ADDITIONAL_PROPERTIES - elif '$ref' in json: - self.ref_type = json['$ref'] - self.type_ = PropertyType.REF - elif 'enum' in json and json.get('type') == 'string': - # Non-string enums (as in the case of [legalValues=(1,2)]) should fall - # through to the next elif. - self.enum_values = [] - for value in json['enum']: - self.enum_values.append(value) - self.type_ = PropertyType.ENUM - elif 'type' in json: - self.type_ = self._JsonTypeToPropertyType(json['type']) - if self.type_ == PropertyType.ARRAY: - self.item_type = Property(self, - name + "Element", - json['items'], - namespace, - from_json=from_json, - from_client=from_client) - elif self.type_ == PropertyType.OBJECT: - # These members are read when this OBJECT Property is used as a Type - type_ = Type(self, self.name, json, namespace) - # self.properties will already have some value from |_AddProperties|. - self.properties.update(type_.properties) - self.functions = type_.functions - elif self.type_ == PropertyType.FUNCTION: - for p in json.get('parameters', []): - self.params.append(Property(self, - p['name'], - p, - namespace, - from_json=from_json, - from_client=from_client)) - if 'returns' in json: - self.returns = Property(self, 'return', json['returns'], namespace) - elif 'choices' in json: - if not json['choices'] or len(json['choices']) == 0: - raise ParseException(self, 'Choices has no choices') - self.choices = {} - self.type_ = PropertyType.CHOICES - self.compiled_type = self.type_ - for choice_json in json['choices']: - choice = Property(self, - self.name, - choice_json, - namespace, - from_json=from_json, - from_client=from_client) - choice.unix_name = UnixName(self.name + choice.type_.name) - # The existence of any single choice is optional - choice.optional = True - self.choices[choice.type_] = choice - elif 'value' in json: - self.has_value = True - self.value = json['value'] - if type(self.value) == int: - self.type_ = PropertyType.INTEGER - self.compiled_type = self.type_ - else: - # TODO(kalman): support more types as necessary. - raise ParseException( - self, '"%s" is not a supported type' % type(self.value)) - else: - raise ParseException( - self, 'Property has no type, $ref, choices, or value') - if 'compiled_type' in json: - if 'type' in json: - self.compiled_type = self._JsonTypeToPropertyType(json['compiled_type']) - else: - raise ParseException(self, 'Property has compiled_type but no type') - else: - self.compiled_type = self.type_ - - def _JsonTypeToPropertyType(self, json_type): - try: - return { - 'any': PropertyType.ANY, - 'array': PropertyType.ARRAY, - 'binary': PropertyType.BINARY, - 'boolean': PropertyType.BOOLEAN, - 'integer': PropertyType.INTEGER, - 'int64': PropertyType.INT64, - 'function': PropertyType.FUNCTION, - 'number': PropertyType.DOUBLE, - 'object': PropertyType.OBJECT, - 'string': PropertyType.STRING, - }[json_type] - except KeyError: - raise NotImplementedError('Type %s not recognized' % json_type) + self.origin = origin + if not isinstance(type_, Type): + raise ValueError("not Type: %s" % type_) + self.type_ = type_ + self.returns = returns + if instance_of is not None: + self.instance_of = instance_of + self.value = value def GetUnixName(self): """Gets the property's unix_name. Raises AttributeError if not set. @@ -359,14 +352,6 @@ class Property(object): (self.name, self._unix_name)) self._unix_name = unix_name - def Copy(self): - """Makes a copy of this model.Property object and allow the unix_name to be - set again. - """ - property_copy = copy.copy(self) - property_copy._unix_name_used = False - return property_copy - unix_name = property(GetUnixName, SetUnixName) class _Enum(object): @@ -403,20 +388,19 @@ class _PropertyTypeInfo(_Enum): class PropertyType(object): """Enum of different types of properties/parameters. """ - INTEGER = _PropertyTypeInfo(True, "INTEGER") - INT64 = _PropertyTypeInfo(True, "INT64") - DOUBLE = _PropertyTypeInfo(True, "DOUBLE") - BOOLEAN = _PropertyTypeInfo(True, "BOOLEAN") - STRING = _PropertyTypeInfo(True, "STRING") - ENUM = _PropertyTypeInfo(False, "ENUM") - ARRAY = _PropertyTypeInfo(False, "ARRAY") - REF = _PropertyTypeInfo(False, "REF") - CHOICES = _PropertyTypeInfo(False, "CHOICES") - OBJECT = _PropertyTypeInfo(False, "OBJECT") - FUNCTION = _PropertyTypeInfo(False, "FUNCTION") - BINARY = _PropertyTypeInfo(False, "BINARY") - ANY = _PropertyTypeInfo(False, "ANY") - ADDITIONAL_PROPERTIES = _PropertyTypeInfo(False, "ADDITIONAL_PROPERTIES") + INTEGER = _PropertyTypeInfo(True, "integer") + INT64 = _PropertyTypeInfo(True, "int64") + DOUBLE = _PropertyTypeInfo(True, "double") + BOOLEAN = _PropertyTypeInfo(True, "boolean") + STRING = _PropertyTypeInfo(True, "string") + ENUM = _PropertyTypeInfo(False, "enum") + ARRAY = _PropertyTypeInfo(False, "array") + REF = _PropertyTypeInfo(False, "ref") + CHOICES = _PropertyTypeInfo(False, "choices") + OBJECT = _PropertyTypeInfo(False, "object") + FUNCTION = _PropertyTypeInfo(False, "function") + BINARY = _PropertyTypeInfo(False, "binary") + ANY = _PropertyTypeInfo(False, "any") def UnixName(name): """Returns the unix_style name for a given lowerCamelCase string. @@ -436,57 +420,57 @@ def _StripNamespace(name, namespace): def _GetModelHierarchy(entity): """Returns the hierarchy of the given model entity.""" hierarchy = [] - while entity: - try: - hierarchy.append(entity.name) - except AttributeError: - hierarchy.append(repr(entity)) - entity = entity.parent + while entity is not None: + hierarchy.append(getattr(entity, 'name', repr(entity))) + if isinstance(entity, Namespace): + hierarchy.insert(0, ' in %s' % entity.source_file) + entity = getattr(entity, 'parent', None) hierarchy.reverse() return hierarchy -def _AddTypes(model, json, namespace): - """Adds Type objects to |model| contained in the 'types' field of |json|. +def _GetTypes(parent, json, namespace, origin): + """Creates Type objects extracted from |json|. """ - model.types = OrderedDict() + types = OrderedDict() for type_json in json.get('types', []): - type_ = Type(model, type_json['id'], type_json, namespace) - model.types[type_.name] = type_ + type_ = Type(parent, type_json['id'], type_json, namespace, origin) + types[type_.name] = type_ + return types -def _AddFunctions(model, json, namespace): - """Adds Function objects to |model| contained in the 'functions' field of - |json|. +def _GetFunctions(parent, json, namespace): + """Creates Function objects extracted from |json|. """ - model.functions = OrderedDict() + functions = OrderedDict() for function_json in json.get('functions', []): - function = Function(model, function_json, namespace, from_json=True) - model.functions[function.name] = function - -def _AddEvents(model, json, namespace): - """Adds Function objects to |model| contained in the 'events' field of |json|. + function = Function(parent, + function_json['name'], + function_json, + namespace, + Origin(from_json=True)) + functions[function.name] = function + return functions + +def _GetEvents(parent, json, namespace): + """Creates Function objects generated from the events in |json|. """ - model.events = OrderedDict() + events = OrderedDict() for event_json in json.get('events', []): - event = Function(model, event_json, namespace, from_client=True) - model.events[event.name] = event - -def _AddProperties(model, - json, - namespace, - from_json=False, - from_client=False): - """Adds model.Property objects to |model| contained in the 'properties' field - of |json|. + event = Function(parent, + event_json['name'], + event_json, + namespace, + Origin(from_client=True)) + events[event.name] = event + return events + +def _GetProperties(parent, json, namespace, origin): + """Generates Property objects extracted from |json|. """ - model.properties = OrderedDict() + properties = OrderedDict() for name, property_json in json.get('properties', {}).items(): - model.properties[name] = Property( - model, - name, - property_json, - namespace, - from_json=from_json, - from_client=from_client) + properties[name] = Property.FromJSON( + parent, name, property_json, namespace, origin) + return properties class _PlatformInfo(_Enum): def __init__(self, name): diff --git a/tools/json_schema_compiler/model_test.py b/tools/json_schema_compiler/model_test.py index 8cd43e8..e310d5e 100755 --- a/tools/json_schema_compiler/model_test.py +++ b/tools/json_schema_compiler/model_test.py @@ -32,41 +32,45 @@ class ModelTest(unittest.TestCase): sorted(self.permissions.functions.keys())) def testHasTypes(self): - self.assertEquals(['tabs.Tab'], self.tabs.types.keys()) - self.assertEquals(['permissions.Permissions'], - self.permissions.types.keys()) - self.assertEquals(['windows.Window'], self.windows.types.keys()) + self.assertEquals(['Tab'], self.tabs.types.keys()) + self.assertEquals(['Permissions'], self.permissions.types.keys()) + self.assertEquals(['Window'], self.windows.types.keys()) def testHasProperties(self): self.assertEquals(["active", "favIconUrl", "highlighted", "id", "incognito", "index", "pinned", "selected", "status", "title", "url", "windowId"], - sorted(self.tabs.types['tabs.Tab'].properties.keys())) + sorted(self.tabs.types['Tab'].properties.keys())) def testProperties(self): - string_prop = self.tabs.types['tabs.Tab'].properties['status'] - self.assertEquals(model.PropertyType.STRING, string_prop.type_) - integer_prop = self.tabs.types['tabs.Tab'].properties['id'] - self.assertEquals(model.PropertyType.INTEGER, integer_prop.type_) - array_prop = self.windows.types['windows.Window'].properties['tabs'] - self.assertEquals(model.PropertyType.ARRAY, array_prop.type_) - self.assertEquals(model.PropertyType.REF, array_prop.item_type.type_) - self.assertEquals('tabs.Tab', array_prop.item_type.ref_type) + string_prop = self.tabs.types['Tab'].properties['status'] + self.assertEquals(model.PropertyType.STRING, + string_prop.type_.property_type) + integer_prop = self.tabs.types['Tab'].properties['id'] + self.assertEquals(model.PropertyType.INTEGER, + integer_prop.type_.property_type) + array_prop = self.windows.types['Window'].properties['tabs'] + self.assertEquals(model.PropertyType.ARRAY, + array_prop.type_.property_type) + self.assertEquals(model.PropertyType.REF, + array_prop.type_.item_type.property_type) + self.assertEquals('tabs.Tab', array_prop.type_.item_type.ref_type) object_prop = self.tabs.functions['query'].params[0] - self.assertEquals(model.PropertyType.OBJECT, object_prop.type_) + self.assertEquals(model.PropertyType.OBJECT, + object_prop.type_.property_type) self.assertEquals( ["active", "highlighted", "pinned", "status", "title", "url", "windowId", "windowType"], - sorted(object_prop.properties.keys())) + sorted(object_prop.type_.properties.keys())) def testChoices(self): self.assertEquals(model.PropertyType.CHOICES, - self.tabs.functions['move'].params[0].type_) + self.tabs.functions['move'].params[0].type_.property_type) def testPropertyNotImplemented(self): (self.permissions_json[0]['types'][0] ['properties']['permissions']['type']) = 'something' - self.assertRaises(NotImplementedError, self.model.AddNamespace, + self.assertRaises(model.ParseException, self.model.AddNamespace, self.permissions_json[0], 'path/to/something.json') def testDescription(self): @@ -78,12 +82,6 @@ class ModelTest(unittest.TestCase): def testPropertyUnixName(self): param = self.tabs.functions['move'].params[0] self.assertEquals('tab_ids', param.unix_name) - param.choices[model.PropertyType.INTEGER].unix_name = 'asdf' - param.choices[model.PropertyType.INTEGER].unix_name = 'tab_ids_integer' - self.assertEquals('tab_ids_integer', - param.choices[model.PropertyType.INTEGER].unix_name) - self.assertRaises(AttributeError, - param.choices[model.PropertyType.INTEGER].SetUnixName, 'breakage') def testUnixName(self): expectations = { diff --git a/tools/json_schema_compiler/preview.py b/tools/json_schema_compiler/preview.py index d577715..300983a 100755 --- a/tools/json_schema_compiler/preview.py +++ b/tools/json_schema_compiler/preview.py @@ -249,7 +249,7 @@ updateEverything(); except (TypeError, KeyError, AttributeError, AssertionError, NotImplementedError) as error: body.Append('<pre>') - body.Append('compiler error: ' + str(error)) + body.Append('compiler error: %s' % error) body.Append('Check server log for more details') body.Append('</pre>') raise diff --git a/tools/json_schema_compiler/schema_util.py b/tools/json_schema_compiler/schema_util.py index dc84e61..e71be8b 100644 --- a/tools/json_schema_compiler/schema_util.py +++ b/tools/json_schema_compiler/schema_util.py @@ -19,32 +19,6 @@ def StripSchemaNamespace(s): return s[last_dot + 1:] return s -def PrefixSchemasWithNamespace(schemas): - for s in schemas: - _PrefixWithNamespace(s.get("namespace"), s) - -def _MaybePrefixFieldWithNamespace(namespace, schema, key): - if json_parse.IsDict(schema) and key in schema: - old_value = schema[key] - if not "." in old_value: - schema[key] = namespace + "." + old_value - -def _PrefixTypesWithNamespace(namespace, types): - for t in types: - _MaybePrefixFieldWithNamespace(namespace, t, "id") - _MaybePrefixFieldWithNamespace(namespace, t, "customBindings") - -def _PrefixWithNamespace(namespace, schema): - if json_parse.IsDict(schema): - if "types" in schema: - _PrefixTypesWithNamespace(namespace, schema.get("types")) - _MaybePrefixFieldWithNamespace(namespace, schema, "$ref") - for s in schema: - _PrefixWithNamespace(namespace, schema[s]) - elif type(schema) == list: - for s in schema: - _PrefixWithNamespace(namespace, s) - def JsFunctionNameToClassName(namespace_name, function_name): """Transform a fully qualified function name like foo.bar.baz into FooBarBaz diff --git a/tools/json_schema_compiler/schema_util_test.py b/tools/json_schema_compiler/schema_util_test.py index 1fa1803..4db88f1 100755 --- a/tools/json_schema_compiler/schema_util_test.py +++ b/tools/json_schema_compiler/schema_util_test.py @@ -4,7 +4,6 @@ # found in the LICENSE file. from schema_util import JsFunctionNameToClassName -from schema_util import PrefixSchemasWithNamespace from schema_util import StripSchemaNamespace import unittest @@ -13,59 +12,6 @@ class SchemaUtilTest(unittest.TestCase): self.assertEquals('Bar', StripSchemaNamespace('foo.Bar')) self.assertEquals('Baz', StripSchemaNamespace('Baz')) - def testPrefixSchemasWithNamespace(self): - schemas = [ - { 'namespace': 'n1', - 'types': [ - { - 'id': 'T1', - 'customBindings': 'T1', - 'properties': { - 'p1': {'$ref': 'T1'}, - 'p2': {'$ref': 'fully.qualified.T'}, - } - } - ], - 'functions': [ - { - 'parameters': [ - { '$ref': 'T1' }, - { '$ref': 'fully.qualified.T' }, - ], - 'returns': { '$ref': 'T1' } - }, - ], - 'events': [ - { - 'parameters': [ - { '$ref': 'T1' }, - { '$ref': 'fully.qualified.T' }, - ], - }, - ], - }, - ] - PrefixSchemasWithNamespace(schemas) - self.assertEquals('n1.T1', schemas[0]['types'][0]['id']) - self.assertEquals('n1.T1', schemas[0]['types'][0]['customBindings']) - self.assertEquals('n1.T1', - schemas[0]['types'][0]['properties']['p1']['$ref']) - self.assertEquals('fully.qualified.T', - schemas[0]['types'][0]['properties']['p2']['$ref']) - - self.assertEquals('n1.T1', - schemas[0]['functions'][0]['parameters'][0]['$ref']) - self.assertEquals('fully.qualified.T', - schemas[0]['functions'][0]['parameters'][1]['$ref']) - self.assertEquals('n1.T1', - schemas[0]['functions'][0]['returns']['$ref']) - - self.assertEquals('n1.T1', - schemas[0]['events'][0]['parameters'][0]['$ref']) - self.assertEquals('fully.qualified.T', - schemas[0]['events'][0]['parameters'][1]['$ref']) - - def testJsFunctionNameToClassName(self): self.assertEquals('FooBar', JsFunctionNameToClassName('foo', 'bar')) self.assertEquals('FooBar', diff --git a/tools/json_schema_compiler/test/additional_properties.json b/tools/json_schema_compiler/test/additional_properties.json index 03bbef8..0b800d9 100644 --- a/tools/json_schema_compiler/test/additional_properties.json +++ b/tools/json_schema_compiler/test/additional_properties.json @@ -44,7 +44,7 @@ "properties": { "integer": {"type": "integer"} }, - "additionalProperties": {"type": "any"} + "additionalProperties": {"type": "string"} } ] } diff --git a/tools/json_schema_compiler/test/additional_properties_unittest.cc b/tools/json_schema_compiler/test/additional_properties_unittest.cc index 4c3d720..5e08d5e 100644 --- a/tools/json_schema_compiler/test/additional_properties_unittest.cc +++ b/tools/json_schema_compiler/test/additional_properties_unittest.cc @@ -2,9 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "tools/json_schema_compiler/test/additional_properties.h" - #include "testing/gtest/include/gtest/gtest.h" +#include "tools/json_schema_compiler/test/additional_properties.h" using namespace test::api::additional_properties; @@ -19,9 +18,7 @@ TEST(JsonSchemaCompilerAdditionalPropertiesTest, type_value->SetInteger("other", 9); type_value->Set("another", list_value.release()); scoped_ptr<AdditionalPropertiesType> type(new AdditionalPropertiesType()); - EXPECT_TRUE(AdditionalPropertiesType::Populate(*type_value, type.get())); - EXPECT_EQ("value", type->string); - EXPECT_TRUE(type_value->Remove("string", NULL)); + ASSERT_TRUE(AdditionalPropertiesType::Populate(*type_value, type.get())); EXPECT_TRUE(type->additional_properties.Equals(type_value.get())); } { @@ -48,22 +45,19 @@ TEST(JsonSchemaCompilerAdditionalPropertiesTest, TEST(JsonSchemaCompilerAdditionalPropertiesTest, ReturnAdditionalPropertiesResultCreate) { - DictionaryValue additional; - additional.SetString("key", "value"); ReturnAdditionalProperties::Results::ResultObject result_object; result_object.integer = 5; - result_object.additional_properties.MergeDictionary(&additional); - scoped_ptr<ListValue> results = - ReturnAdditionalProperties::Results::Create(result_object); - DictionaryValue* result_dict = NULL; - EXPECT_TRUE(results->GetDictionary(0, &result_dict)); + result_object.additional_properties["key"] = "value"; - Value* int_temp_value_out = NULL; - int int_temp = 0; - EXPECT_TRUE(result_dict->Remove("integer", &int_temp_value_out)); - scoped_ptr<Value> int_temp_value(int_temp_value_out); - EXPECT_TRUE(int_temp_value->GetAsInteger(&int_temp)); - EXPECT_EQ(5, int_temp); + ListValue expected; + { + DictionaryValue* dict = new DictionaryValue(); + dict->SetInteger("integer", 5); + dict->SetString("key", "value"); + expected.Append(dict); + } - EXPECT_TRUE(result_dict->Equals(&additional)); + EXPECT_TRUE(Value::Equals( + ReturnAdditionalProperties::Results::Create(result_object).get(), + &expected)); } diff --git a/tools/json_schema_compiler/test/any_unittest.cc b/tools/json_schema_compiler/test/any_unittest.cc index 0780230..81fd681 100644 --- a/tools/json_schema_compiler/test/any_unittest.cc +++ b/tools/json_schema_compiler/test/any_unittest.cc @@ -2,9 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "tools/json_schema_compiler/test/any.h" - #include "testing/gtest/include/gtest/gtest.h" +#include "tools/json_schema_compiler/test/any.h" using namespace test::api::any; @@ -41,9 +40,9 @@ TEST(JsonSchemaCompilerAnyTest, OptionalAnyParamsCreate) { params_value->Append(param->DeepCopy()); scoped_ptr<OptionalAny::Params> params( OptionalAny::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_TRUE(params->any_name.get()); - EXPECT_TRUE(params->any_name->value().Equals(param.get())); + ASSERT_TRUE(params); + ASSERT_TRUE(params->any_name); + EXPECT_TRUE(params->any_name->Equals(param.get())); } { scoped_ptr<ListValue> params_value(new ListValue()); @@ -51,9 +50,8 @@ TEST(JsonSchemaCompilerAnyTest, OptionalAnyParamsCreate) { params_value->Append(param->DeepCopy()); scoped_ptr<OptionalAny::Params> params( OptionalAny::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_TRUE(params->any_name.get()); - EXPECT_TRUE(params->any_name.get()); - EXPECT_TRUE(params->any_name->value().Equals(param.get())); + ASSERT_TRUE(params); + ASSERT_TRUE(params->any_name); + EXPECT_TRUE(params->any_name->Equals(param.get())); } } diff --git a/tools/json_schema_compiler/test/arrays.json b/tools/json_schema_compiler/test/arrays.json index c5fbe9b..a0edd54 100644 --- a/tools/json_schema_compiler/test/arrays.json +++ b/tools/json_schema_compiler/test/arrays.json @@ -116,7 +116,7 @@ "type": "array", "items": { "type": "object", - "additionalProperties": {"type": "any"} + "additionalProperties": {"type": "integer"} } }, { @@ -144,6 +144,53 @@ ] }, { + "name": "justChoices", + "type": "function", + "description": "Takes some Choices.", + "parameters": [ + { + "name": "choices", + "choices": [ + { "type": "integer" }, + { "type": "boolean" }, + { "type": "array", + "items": {"$ref": "Item"} + } + ] + }, + { + "name": "callback", + "type": "function", + "parameters": [] + } + ] + }, + { + "name": "choicesArray", + "type": "function", + "description": "Takes some Choices.", + "parameters": [ + { + "name": "choices", + "type": "array", + "items": { + "choices": [ + { "type": "integer" }, + { "type": "boolean" }, + { "type": "array", + "items": {"$ref": "Item"} + } + ] + } + }, + { + "name": "callback", + "type": "function", + "parameters": [] + } + ] + }, + { "name": "returnIntegerArray", "type": "function", "description": "Returns some integers.", diff --git a/tools/json_schema_compiler/test/arrays_unittest.cc b/tools/json_schema_compiler/test/arrays_unittest.cc index c6d1e06..e480c8e 100644 --- a/tools/json_schema_compiler/test/arrays_unittest.cc +++ b/tools/json_schema_compiler/test/arrays_unittest.cc @@ -46,45 +46,47 @@ TEST(JsonSchemaCompilerArrayTest, BasicArrayType) { { scoped_ptr<DictionaryValue> value = CreateBasicArrayTypeDictionary(); scoped_ptr<BasicArrayType> basic_array_type(new BasicArrayType()); - EXPECT_TRUE(BasicArrayType::Populate(*value, basic_array_type.get())); + ASSERT_TRUE(BasicArrayType::Populate(*value, basic_array_type.get())); EXPECT_TRUE(value->Equals(basic_array_type->ToValue().get())); } } TEST(JsonSchemaCompilerArrayTest, EnumArrayType) { - std::vector<EnumArrayType::TypesElement> enums; - enums.push_back(EnumArrayType::TYPES_ELEMENT_ONE); - enums.push_back(EnumArrayType::TYPES_ELEMENT_TWO); - enums.push_back(EnumArrayType::TYPES_ELEMENT_THREE); + std::vector<EnumArrayType::TypesType> enums; + enums.push_back(EnumArrayType::TYPES_TYPE_ONE); + enums.push_back(EnumArrayType::TYPES_TYPE_TWO); + enums.push_back(EnumArrayType::TYPES_TYPE_THREE); scoped_ptr<ListValue> types(new ListValue()); for (size_t i = 0; i < enums.size(); ++i) - types->Append(EnumArrayType::CreateEnumValue(enums[i]).release()); + types->Append(new base::StringValue(EnumArrayType::ToString(enums[i]))); DictionaryValue value; value.Set("types", types.release()); EnumArrayType enum_array_type; - EXPECT_TRUE(EnumArrayType::Populate(value, &enum_array_type)); + ASSERT_TRUE(EnumArrayType::Populate(value, &enum_array_type)); EXPECT_EQ(enums, enum_array_type.types); } TEST(JsonSchemaCompilerArrayTest, OptionalEnumArrayType) { { - std::vector<OptionalEnumArrayType::TypesElement> enums; - enums.push_back(OptionalEnumArrayType::TYPES_ELEMENT_ONE); - enums.push_back(OptionalEnumArrayType::TYPES_ELEMENT_TWO); - enums.push_back(OptionalEnumArrayType::TYPES_ELEMENT_THREE); + std::vector<OptionalEnumArrayType::TypesType> enums; + enums.push_back(OptionalEnumArrayType::TYPES_TYPE_ONE); + enums.push_back(OptionalEnumArrayType::TYPES_TYPE_TWO); + enums.push_back(OptionalEnumArrayType::TYPES_TYPE_THREE); scoped_ptr<ListValue> types(new ListValue()); - for (size_t i = 0; i < enums.size(); ++i) - types->Append(OptionalEnumArrayType::CreateEnumValue(enums[i]).release()); + for (size_t i = 0; i < enums.size(); ++i) { + types->Append(new base::StringValue( + OptionalEnumArrayType::ToString(enums[i]))); + } DictionaryValue value; value.Set("types", types.release()); OptionalEnumArrayType enum_array_type; - EXPECT_TRUE(OptionalEnumArrayType::Populate(value, &enum_array_type)); + ASSERT_TRUE(OptionalEnumArrayType::Populate(value, &enum_array_type)); EXPECT_EQ(enums, *enum_array_type.types); } { @@ -94,7 +96,7 @@ TEST(JsonSchemaCompilerArrayTest, OptionalEnumArrayType) { value.Set("types", enum_array.release()); OptionalEnumArrayType enum_array_type; - EXPECT_FALSE(OptionalEnumArrayType::Populate(value, &enum_array_type)); + ASSERT_FALSE(OptionalEnumArrayType::Populate(value, &enum_array_type)); EXPECT_TRUE(enum_array_type.types->empty()); } } @@ -153,7 +155,7 @@ TEST(JsonSchemaCompilerArrayTest, AnyArrayParamsCreate) { EXPECT_TRUE(params.get()); ASSERT_EQ(3u, params->anys.size()); int int_temp = 0; - EXPECT_TRUE(params->anys[0]->value().GetAsInteger(&int_temp)); + EXPECT_TRUE(params->anys[0]->GetAsInteger(&int_temp)); EXPECT_EQ(1, int_temp); } @@ -167,13 +169,8 @@ TEST(JsonSchemaCompilerArrayTest, ObjectArrayParamsCreate) { ObjectArray::Params::Create(*params_value)); EXPECT_TRUE(params.get()); ASSERT_EQ(2u, params->objects.size()); - int object_val = 0; - EXPECT_TRUE(params->objects[0]->additional_properties.GetInteger( - "val", &object_val)); - EXPECT_EQ(1, object_val); - EXPECT_TRUE(params->objects[1]->additional_properties.GetInteger( - "val", &object_val)); - EXPECT_EQ(2, object_val); + EXPECT_EQ(1, params->objects[0]->additional_properties["val"]); + EXPECT_EQ(2, params->objects[1]->additional_properties["val"]); } TEST(JsonSchemaCompilerArrayTest, RefArrayParamsCreate) { diff --git a/tools/json_schema_compiler/test/choices_unittest.cc b/tools/json_schema_compiler/test/choices_unittest.cc index b4e76a1..b8bedf4 100644 --- a/tools/json_schema_compiler/test/choices_unittest.cc +++ b/tools/json_schema_compiler/test/choices_unittest.cc @@ -22,9 +22,8 @@ TEST(JsonSchemaCompilerChoicesTest, TakesIntegersParamsCreate) { scoped_ptr<TakesIntegers::Params> params( TakesIntegers::Params::Create(*params_value)); EXPECT_TRUE(params.get()); - EXPECT_EQ(TakesIntegers::Params::NUMS_INTEGER, params->nums_type); - EXPECT_FALSE(params->nums_array.get()); - EXPECT_EQ(6, *params->nums_integer); + EXPECT_FALSE(params->nums.as_array); + EXPECT_EQ(6, *params->nums.as_integer); } { scoped_ptr<ListValue> params_value(new ListValue()); @@ -34,45 +33,41 @@ TEST(JsonSchemaCompilerChoicesTest, TakesIntegersParamsCreate) { params_value->Append(integers.release()); scoped_ptr<TakesIntegers::Params> params( TakesIntegers::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_EQ(TakesIntegers::Params::NUMS_ARRAY, params->nums_type); - EXPECT_EQ((size_t) 2, (*params->nums_array).size()); - EXPECT_EQ(6, (*params->nums_array)[0]); - EXPECT_EQ(8, (*params->nums_array)[1]); + ASSERT_TRUE(params); + ASSERT_TRUE(params->nums.as_array); + EXPECT_EQ((size_t) 2, params->nums.as_array->size()); + EXPECT_EQ(6, params->nums.as_array->at(0)); + EXPECT_EQ(8, params->nums.as_array->at(1)); } } TEST(JsonSchemaCompilerChoicesTest, ObjectWithChoicesParamsCreate) { { scoped_ptr<DictionaryValue> object_param(new DictionaryValue()); - object_param->SetWithoutPathExpansion("strings", - Value::CreateStringValue("asdf")); + object_param->SetString("strings", "asdf"); scoped_ptr<ListValue> params_value(new ListValue()); params_value->Append(object_param.release()); scoped_ptr<ObjectWithChoices::Params> params( ObjectWithChoices::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_EQ(ObjectWithChoices::Params::StringInfo::STRINGS_STRING, - params->string_info.strings_type); - EXPECT_EQ("asdf", *params->string_info.strings_string); + ASSERT_TRUE(params); + EXPECT_FALSE(params->string_info.strings.as_array); + EXPECT_EQ("asdf", *params->string_info.strings.as_string); + EXPECT_FALSE(params->string_info.integers); } { scoped_ptr<DictionaryValue> object_param(new DictionaryValue()); - object_param->SetWithoutPathExpansion("strings", - Value::CreateStringValue("asdf")); - object_param->SetWithoutPathExpansion("integers", - Value::CreateIntegerValue(6)); + object_param->SetString("strings", "asdf"); + object_param->SetInteger("integers", 6); scoped_ptr<ListValue> params_value(new ListValue()); params_value->Append(object_param.release()); scoped_ptr<ObjectWithChoices::Params> params( ObjectWithChoices::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_EQ(ObjectWithChoices::Params::StringInfo::STRINGS_STRING, - params->string_info.strings_type); - EXPECT_EQ("asdf", *params->string_info.strings_string); - EXPECT_EQ(ObjectWithChoices::Params::StringInfo::INTEGERS_INTEGER, - params->string_info.integers_type); - EXPECT_EQ(6, *params->string_info.integers_integer); + ASSERT_TRUE(params); + EXPECT_FALSE(params->string_info.strings.as_array); + EXPECT_EQ("asdf", *params->string_info.strings.as_string); + ASSERT_TRUE(params->string_info.integers); + EXPECT_FALSE(params->string_info.integers->as_array); + EXPECT_EQ(6, *params->string_info.integers->as_integer); } } @@ -127,15 +122,13 @@ TEST(JsonSchemaCompilerChoicesTest, PopulateChoiceType) { ChoiceType out; ASSERT_TRUE(ChoiceType::Populate(value, &out)); - EXPECT_EQ(ChoiceType::INTEGERS_INTEGER, out.integers_type); - ASSERT_TRUE(out.integers_integer.get()); - EXPECT_FALSE(out.integers_array.get()); - EXPECT_EQ(4, *out.integers_integer); - - EXPECT_EQ(ChoiceType::STRINGS_ARRAY, out.strings_type); - EXPECT_FALSE(out.strings_string.get()); - ASSERT_TRUE(out.strings_array.get()); - EXPECT_EQ(strings, *out.strings_array); + ASSERT_TRUE(out.integers.as_integer.get()); + EXPECT_FALSE(out.integers.as_array.get()); + EXPECT_EQ(4, *out.integers.as_integer); + + EXPECT_FALSE(out.strings->as_string.get()); + ASSERT_TRUE(out.strings->as_array.get()); + EXPECT_EQ(strings, *out.strings->as_array); } TEST(JsonSchemaCompilerChoicesTest, ChoiceTypeToValue) { @@ -156,23 +149,29 @@ TEST(JsonSchemaCompilerChoicesTest, ChoiceTypeToValue) { TEST(JsonSchemaCompilerChoicesTest, ReturnChoices) { { - std::vector<int> integers; - integers.push_back(1); - integers.push_back(2); - scoped_ptr<ListValue> array_results = - ReturnChoices::Results::Create(integers); - - ListValue expected; - ListValue* expected_argument = new ListValue(); - expected_argument->Append(Value::CreateIntegerValue(1)); - expected_argument->Append(Value::CreateIntegerValue(2)); - expected.Append(expected_argument); - EXPECT_TRUE(array_results->Equals(&expected)); + ReturnChoices::Results::Result results; + results.as_array.reset(new std::vector<int>()); + results.as_array->push_back(1); + results.as_array->push_back(2); + + scoped_ptr<base::Value> results_value = results.ToValue(); + ASSERT_TRUE(results_value); + + base::ListValue expected; + expected.AppendInteger(1); + expected.AppendInteger(2); + + EXPECT_TRUE(expected.Equals(results_value.get())); } { - scoped_ptr<ListValue> integer_results = ReturnChoices::Results::Create(5); - ListValue expected; - expected.Append(Value::CreateIntegerValue(5)); - EXPECT_TRUE(integer_results->Equals(&expected)); + ReturnChoices::Results::Result results; + results.as_integer.reset(new int(5)); + + scoped_ptr<base::Value> results_value = results.ToValue(); + ASSERT_TRUE(results_value); + + base::FundamentalValue expected(5); + + EXPECT_TRUE(expected.Equals(results_value.get())); } } diff --git a/tools/json_schema_compiler/test/enums.json b/tools/json_schema_compiler/test/enums.json index a0bea46..6769b19 100644 --- a/tools/json_schema_compiler/test/enums.json +++ b/tools/json_schema_compiler/test/enums.json @@ -4,6 +4,7 @@ "types": [ { "id": "Enumeration", + "type": "string", "enum": ["one", "two", "three"] }, { diff --git a/tools/json_schema_compiler/test/enums_unittest.cc b/tools/json_schema_compiler/test/enums_unittest.cc index 4604694..81bfe8d 100644 --- a/tools/json_schema_compiler/test/enums_unittest.cc +++ b/tools/json_schema_compiler/test/enums_unittest.cc @@ -33,10 +33,10 @@ TEST(JsonSchemaCompilerEnumsTest, EnumsAsTypes) { scoped_ptr<TakesEnumAsType::Params> params( TakesEnumAsType::Params::Create(args)); ASSERT_TRUE(params.get()); - EXPECT_EQ(ENUMS_ENUMERATION_ONE, params->enumeration); + EXPECT_EQ(ENUMERATION_ONE, params->enumeration); EXPECT_TRUE(args.Equals(ReturnsEnumAsType::Results::Create( - ENUMS_ENUMERATION_ONE).get())); + ENUMERATION_ONE).get())); } { HasEnumeration enumeration; @@ -56,7 +56,8 @@ TEST(JsonSchemaCompilerEnumsTest, EnumsAsTypes) { TEST(JsonSchemaCompilerEnumsTest, ReturnsEnumCreate) { { ReturnsEnum::Results::State state = ReturnsEnum::Results::STATE_FOO; - scoped_ptr<Value> result = ReturnsEnum::Results::CreateEnumValue(state); + scoped_ptr<Value> result( + new base::StringValue(ReturnsEnum::Results::ToString(state))); scoped_ptr<Value> expected(Value::CreateStringValue("foo")); EXPECT_TRUE(result->Equals(expected.get())); } @@ -189,7 +190,8 @@ TEST(JsonSchemaCompilerEnumsTest, TakesMultipleOptionalEnumsParamsCreate) { TEST(JsonSchemaCompilerEnumsTest, OnEnumFiredCreate) { { OnEnumFired::SomeEnum some_enum = OnEnumFired::SOME_ENUM_FOO; - scoped_ptr<Value> result(OnEnumFired::CreateEnumValue(some_enum)); + scoped_ptr<Value> result( + new base::StringValue(OnEnumFired::ToString(some_enum))); scoped_ptr<Value> expected(Value::CreateStringValue("foo")); EXPECT_TRUE(result->Equals(expected.get())); } diff --git a/tools/json_schema_compiler/test/functions_on_types_unittest.cc b/tools/json_schema_compiler/test/functions_on_types_unittest.cc index 73a3cf2..6fe4526 100644 --- a/tools/json_schema_compiler/test/functions_on_types_unittest.cc +++ b/tools/json_schema_compiler/test/functions_on_types_unittest.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "tools/json_schema_compiler/test/functions_on_types.h" - +#include "base/values.h" #include "testing/gtest/include/gtest/gtest.h" +#include "tools/json_schema_compiler/test/functions_on_types.h" using namespace test::api::functions_on_types; @@ -13,24 +13,24 @@ TEST(JsonSchemaCompilerFunctionsOnTypesTest, StorageAreaGetParamsCreate) { scoped_ptr<ListValue> params_value(new ListValue()); scoped_ptr<StorageArea::Get::Params> params( StorageArea::Get::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_EQ(StorageArea::Get::Params::KEYS_NONE, params->keys_type); + ASSERT_TRUE(params); + EXPECT_FALSE(params->keys); } { scoped_ptr<ListValue> params_value(new ListValue()); params_value->Append(Value::CreateIntegerValue(9)); scoped_ptr<StorageArea::Get::Params> params( StorageArea::Get::Params::Create(*params_value)); - EXPECT_FALSE(params.get()); + EXPECT_FALSE(params); } { scoped_ptr<ListValue> params_value(new ListValue()); params_value->Append(Value::CreateStringValue("test")); scoped_ptr<StorageArea::Get::Params> params( StorageArea::Get::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_EQ(StorageArea::Get::Params::KEYS_STRING, params->keys_type); - EXPECT_EQ("test", *params->keys_string); + ASSERT_TRUE(params); + ASSERT_TRUE(params->keys); + EXPECT_EQ("test", *params->keys->as_string); } { scoped_ptr<DictionaryValue> keys_object_value(new DictionaryValue()); @@ -40,10 +40,10 @@ TEST(JsonSchemaCompilerFunctionsOnTypesTest, StorageAreaGetParamsCreate) { params_value->Append(keys_object_value->DeepCopy()); scoped_ptr<StorageArea::Get::Params> params( StorageArea::Get::Params::Create(*params_value)); - EXPECT_TRUE(params.get()); - EXPECT_EQ(StorageArea::Get::Params::KEYS_OBJECT, params->keys_type); - EXPECT_TRUE( - keys_object_value->Equals(¶ms->keys_object->additional_properties)); + ASSERT_TRUE(params); + ASSERT_TRUE(params->keys); + EXPECT_TRUE(keys_object_value->Equals( + ¶ms->keys->as_object->additional_properties)); } } @@ -53,7 +53,7 @@ TEST(JsonSchemaCompilerFunctionsOnTypesTest, StorageAreaGetResultCreate) { items.additional_properties.SetString("sdfg", "zxcv"); scoped_ptr<ListValue> results = StorageArea::Get::Results::Create(items); DictionaryValue* item_result = NULL; - results->GetDictionary(0, &item_result); + ASSERT_TRUE(results->GetDictionary(0, &item_result)); EXPECT_TRUE(item_result->Equals(&items.additional_properties)); } diff --git a/tools/json_schema_compiler/test/idl_schemas_unittest.cc b/tools/json_schema_compiler/test/idl_schemas_unittest.cc index 8afb59c..5186be9 100644 --- a/tools/json_schema_compiler/test/idl_schemas_unittest.cc +++ b/tools/json_schema_compiler/test/idl_schemas_unittest.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "base/values.h" -#include "tools/json_schema_compiler/any.h" #include "tools/json_schema_compiler/test/idl_basics.h" #include "tools/json_schema_compiler/test/idl_object_types.h" @@ -167,13 +166,12 @@ TEST(IdlCompiler, ObjectTypes) { // Test the BarType type. BarType b1; - base::FundamentalValue seven(7); - b1.x.Init(seven); + b1.x.reset(new base::FundamentalValue(7)); scoped_ptr<DictionaryValue> serialized_bar = b1.ToValue(); BarType b2; EXPECT_TRUE(BarType::Populate(*serialized_bar.get(), &b2)); int tmp_int = 0; - EXPECT_TRUE(b2.x.value().GetAsInteger(&tmp_int)); + EXPECT_TRUE(b2.x->GetAsInteger(&tmp_int)); EXPECT_EQ(7, tmp_int); // Test the params to the ObjectFunction1 function. diff --git a/tools/json_schema_compiler/test/objects_unittest.cc b/tools/json_schema_compiler/test/objects_unittest.cc index 3582f4f..5a4248d 100644 --- a/tools/json_schema_compiler/test/objects_unittest.cc +++ b/tools/json_schema_compiler/test/objects_unittest.cc @@ -68,9 +68,4 @@ TEST(JsonSchemaCompilerObjectsTest, OnObjectFiredCreate) { DictionaryValue* result = NULL; ASSERT_TRUE(results->GetDictionary(0, &result)); ASSERT_TRUE(result->Equals(&expected)); - - std::string json1 = OnObjectFired::ToJson(object); - std::string json2; - base::JSONWriter::Write(results.get(), &json2); - ASSERT_EQ(json1, json2); } diff --git a/tools/json_schema_compiler/util.cc b/tools/json_schema_compiler/util.cc index ca5c25a..25f908c 100644 --- a/tools/json_schema_compiler/util.cc +++ b/tools/json_schema_compiler/util.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "tools/json_schema_compiler/any.h" #include "tools/json_schema_compiler/util.h" #include "base/values.h" @@ -26,14 +25,13 @@ bool GetItemFromList(const ListValue& from, int index, std::string* out) { return from.GetString(index, out); } -bool GetItemFromList(const ListValue& from, int index, - linked_ptr<any::Any>* out) { - const Value* value = NULL; +bool GetItemFromList(const ListValue& from, + int index, + linked_ptr<base::Value>* out) { + const base::Value* value = NULL; if (!from.Get(index, &value)) return false; - scoped_ptr<any::Any> any_object(new any::Any()); - any_object->Init(*value); - *out = linked_ptr<any::Any>(any_object.release()); + *out = make_linked_ptr(value->DeepCopy()); return true; } @@ -42,30 +40,35 @@ bool GetItemFromList(const ListValue& from, int index, const DictionaryValue* dict = NULL; if (!from.GetDictionary(index, &dict)) return false; - *out = linked_ptr<DictionaryValue>(dict->DeepCopy()); + *out = make_linked_ptr(dict->DeepCopy()); return true; } void AddItemToList(const int from, base::ListValue* out) { out->Append(base::Value::CreateIntegerValue(from)); } + void AddItemToList(const bool from, base::ListValue* out) { out->Append(base::Value::CreateBooleanValue(from)); } + void AddItemToList(const double from, base::ListValue* out) { out->Append(base::Value::CreateDoubleValue(from)); } + void AddItemToList(const std::string& from, base::ListValue* out) { out->Append(base::Value::CreateStringValue(from)); } + +void AddItemToList(const linked_ptr<base::Value>& from, + base::ListValue* out) { + out->Append(from->DeepCopy()); +} + void AddItemToList(const linked_ptr<base::DictionaryValue>& from, - base::ListValue* out) { + base::ListValue* out) { out->Append(static_cast<Value*>(from->DeepCopy())); } -void AddItemToList(const linked_ptr<any::Any>& from, - base::ListValue* out) { - out->Append(from->value().DeepCopy()); -} } // namespace api_util } // namespace extensions diff --git a/tools/json_schema_compiler/util.h b/tools/json_schema_compiler/util.h index 6943590..425487d 100644 --- a/tools/json_schema_compiler/util.h +++ b/tools/json_schema_compiler/util.h @@ -14,10 +14,6 @@ namespace json_schema_compiler { -namespace any { -class Any; -} - namespace util { // Creates a new item at |out| from |from|[|index|]. These are used by template @@ -26,10 +22,12 @@ bool GetItemFromList(const ListValue& from, int index, int* out); bool GetItemFromList(const ListValue& from, int index, bool* out); bool GetItemFromList(const ListValue& from, int index, double* out); bool GetItemFromList(const ListValue& from, int index, std::string* out); -bool GetItemFromList(const ListValue& from, int index, - linked_ptr<base::DictionaryValue>* out); -bool GetItemFromList(const ListValue& from, int index, - linked_ptr<any::Any>* out); +bool GetItemFromList(const ListValue& from, + int index, + linked_ptr<base::Value>* out); +bool GetItemFromList(const ListValue& from, + int index, + linked_ptr<base::DictionaryValue>* out); // This template is used for types generated by tools/json_schema_compiler. template<class T> @@ -134,10 +132,10 @@ void AddItemToList(const int from, base::ListValue* out); void AddItemToList(const bool from, base::ListValue* out); void AddItemToList(const double from, base::ListValue* out); void AddItemToList(const std::string& from, base::ListValue* out); +void AddItemToList(const linked_ptr<base::Value>& from, + base::ListValue* out); void AddItemToList(const linked_ptr<base::DictionaryValue>& from, - base::ListValue* out); -void AddItemToList(const linked_ptr<any::Any>& from, - base::ListValue* out); + base::ListValue* out); // This template is used for types generated by tools/json_schema_compiler. template<class T> @@ -170,8 +168,7 @@ void PopulateListFromOptionalArray( } template <class T> -scoped_ptr<Value> CreateValueFromArray( - const std::vector<T>& from) { +scoped_ptr<Value> CreateValueFromArray(const std::vector<T>& from) { base::ListValue* list = new base::ListValue(); PopulateListFromArray(from, list); return scoped_ptr<Value>(list); diff --git a/tools/json_schema_compiler/util_cc_helper.py b/tools/json_schema_compiler/util_cc_helper.py index bf089dd..8d490ba 100644 --- a/tools/json_schema_compiler/util_cc_helper.py +++ b/tools/json_schema_compiler/util_cc_helper.py @@ -25,7 +25,7 @@ class UtilCCHelper(object): 'dst': dst, } - sub['type'] = self._type_manager.GetType(prop) + sub['type'] = self._type_manager.GetCppType(prop), if array_prop.optional: val = ('%(namespace)s::PopulateOptionalArrayFromDictionary' '(*%(src)s, "%(name)s", &%(dst)s)') @@ -46,7 +46,7 @@ class UtilCCHelper(object): 'namespace': API_UTIL_NAMESPACE, 'src': src, 'dst': dst, - 'type': self._type_manager.GetType(prop), + 'type': self._type_manager.GetCppType(prop), } if optional: @@ -65,7 +65,7 @@ class UtilCCHelper(object): sub = { 'namespace': API_UTIL_NAMESPACE, 'src': src, - 'type': self._type_manager.GetType(prop), + 'type': self._type_manager.GetCppType(prop), } if optional: |