JSON Schema Compiler supports functions as PropertyTypes.

Before, the compiler raised an exception when a function was used as a property,
which happens whenever a function is passed as a parameter.

The solution presented here is to create a bool has_<function_name> to allow
the hand-written C++ code to know whether or not the function was passed in.

BUG=138850
TEST=function_as_parameter


Review URL: https://chromiumcodereview.appspot.com/10824002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148480 0039d316-1c4b-4281-b951-d872f2087c98
parent 38b3db9f
...@@ -2039,6 +2039,7 @@ ...@@ -2039,6 +2039,7 @@
'../tools/json_schema_compiler/test/choices_unittest.cc', '../tools/json_schema_compiler/test/choices_unittest.cc',
'../tools/json_schema_compiler/test/crossref_unittest.cc', '../tools/json_schema_compiler/test/crossref_unittest.cc',
'../tools/json_schema_compiler/test/enums_unittest.cc', '../tools/json_schema_compiler/test/enums_unittest.cc',
'../tools/json_schema_compiler/test/functions_as_parameters_unittest.cc',
'../tools/json_schema_compiler/test/functions_on_types_unittest.cc', '../tools/json_schema_compiler/test/functions_on_types_unittest.cc',
'../tools/json_schema_compiler/test/idl_schemas_unittest.cc', '../tools/json_schema_compiler/test/idl_schemas_unittest.cc',
'../tools/json_schema_compiler/test/objects_unittest.cc', '../tools/json_schema_compiler/test/objects_unittest.cc',
......
...@@ -152,6 +152,7 @@ class CCGenerator(object): ...@@ -152,6 +152,7 @@ class CCGenerator(object):
t == PropertyType.CHOICES or t == PropertyType.CHOICES or
t == PropertyType.ENUM or t == PropertyType.ENUM or
t == PropertyType.OBJECT or t == PropertyType.OBJECT or
t == PropertyType.FUNCTION or
t == PropertyType.REF or t == PropertyType.REF or
t == PropertyType.STRING): t == PropertyType.STRING):
# TODO(miket): It would be nice to initialize CHOICES and ENUM, but we # TODO(miket): It would be nice to initialize CHOICES and ENUM, but we
...@@ -316,6 +317,12 @@ class CCGenerator(object): ...@@ -316,6 +317,12 @@ class CCGenerator(object):
return '%s.DeepCopy()' % self._any_helper.GetValue(prop, var) return '%s.DeepCopy()' % self._any_helper.GetValue(prop, var)
elif prop.type_ == PropertyType.ADDITIONAL_PROPERTIES: elif prop.type_ == PropertyType.ADDITIONAL_PROPERTIES:
return '%s.DeepCopy()' % var return '%s.DeepCopy()' % var
elif prop.type_ == PropertyType.FUNCTION:
if prop.optional:
vardot = var + '->'
else:
vardot = var + '.'
return '%sDeepCopy()' % vardot
elif prop.type_ == PropertyType.ENUM: elif prop.type_ == PropertyType.ENUM:
return 'CreateEnumValue(%s).release()' % var return 'CreateEnumValue(%s).release()' % var
elif prop.type_ == PropertyType.BINARY: elif prop.type_ == PropertyType.BINARY:
...@@ -469,6 +476,9 @@ class CCGenerator(object): ...@@ -469,6 +476,9 @@ class CCGenerator(object):
'if (!%(ctype)s::Populate(*dictionary, &%(dst)s->%(name)s))') 'if (!%(ctype)s::Populate(*dictionary, &%(dst)s->%(name)s))')
.Append(' return %(failure_value)s;') .Append(' return %(failure_value)s;')
) )
elif prop.type_ == PropertyType.FUNCTION:
if prop.optional:
c.Append('%(dst)s->%(name)s.reset(new base::DictionaryValue());')
elif prop.type_ == PropertyType.ANY: elif prop.type_ == PropertyType.ANY:
if prop.optional: if prop.optional:
c.Append('%(dst)s->%(name)s.reset(new ' + any_helper.ANY_CLASS + '());') c.Append('%(dst)s->%(name)s.reset(new ' + any_helper.ANY_CLASS + '());')
......
...@@ -152,6 +152,11 @@ class CppTypeGenerator(object): ...@@ -152,6 +152,11 @@ class CppTypeGenerator(object):
cpp_type = any_helper.ANY_CLASS cpp_type = any_helper.ANY_CLASS
elif prop.type_ == PropertyType.OBJECT: elif prop.type_ == PropertyType.OBJECT:
cpp_type = cpp_util.Classname(prop.name) cpp_type = cpp_util.Classname(prop.name)
elif prop.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
# was passed in or not.
cpp_type = 'base::DictionaryValue'
elif prop.type_ == PropertyType.ARRAY: elif prop.type_ == PropertyType.ARRAY:
item_type = prop.item_type item_type = prop.item_type
if item_type.type_ == PropertyType.REF: if item_type.type_ == PropertyType.REF:
......
...@@ -55,6 +55,7 @@ def GetValueType(type_): ...@@ -55,6 +55,7 @@ def GetValueType(type_):
PropertyType.DOUBLE: 'Value::TYPE_DOUBLE', PropertyType.DOUBLE: 'Value::TYPE_DOUBLE',
PropertyType.ENUM: 'Value::TYPE_STRING', PropertyType.ENUM: 'Value::TYPE_STRING',
PropertyType.OBJECT: 'Value::TYPE_DICTIONARY', PropertyType.OBJECT: 'Value::TYPE_DICTIONARY',
PropertyType.FUNCTION: 'Value::TYPE_DICTIONARY',
PropertyType.ARRAY: 'Value::TYPE_LIST', PropertyType.ARRAY: 'Value::TYPE_LIST',
PropertyType.BINARY: 'Value::TYPE_BINARY', PropertyType.BINARY: 'Value::TYPE_BINARY',
}[type_] }[type_]
......
...@@ -158,10 +158,11 @@ class HGenerator(object): ...@@ -158,10 +158,11 @@ class HGenerator(object):
for prop in self._cpp_type_generator.ExpandParams(props): for prop in self._cpp_type_generator.ExpandParams(props):
if prop.description: if prop.description:
c.Comment(prop.description) c.Comment(prop.description)
c.Append('%s %s;' % ( (c.Append('%s %s;' % (
self._cpp_type_generator.GetType(prop, wrap_optional=True), self._cpp_type_generator.GetType(prop, wrap_optional=True),
prop.unix_name)) prop.unix_name))
c.Append() .Append()
)
return c return c
def _GenerateType(self, type_): def _GenerateType(self, type_):
......
...@@ -212,6 +212,8 @@ class Property(object): ...@@ -212,6 +212,8 @@ class Property(object):
# self.properties will already have some value from |_AddProperties|. # self.properties will already have some value from |_AddProperties|.
self.properties.update(type_.properties) self.properties.update(type_.properties)
self.functions = type_.functions self.functions = type_.functions
elif json_type == 'function':
self.type_ = PropertyType.FUNCTION
elif json_type == 'binary': elif json_type == 'binary':
self.type_ = PropertyType.BINARY self.type_ = PropertyType.BINARY
else: else:
...@@ -293,6 +295,7 @@ class PropertyType(object): ...@@ -293,6 +295,7 @@ class PropertyType(object):
REF = _Info(False, "REF") REF = _Info(False, "REF")
CHOICES = _Info(False, "CHOICES") CHOICES = _Info(False, "CHOICES")
OBJECT = _Info(False, "OBJECT") OBJECT = _Info(False, "OBJECT")
FUNCTION = _Info(False, "FUNCTION")
BINARY = _Info(False, "BINARY") BINARY = _Info(False, "BINARY")
ANY = _Info(False, "ANY") ANY = _Info(False, "ANY")
ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES") ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES")
...@@ -350,16 +353,6 @@ def _AddProperties(model, json, from_json=False, from_client=False): ...@@ -350,16 +353,6 @@ def _AddProperties(model, json, from_json=False, from_client=False):
""" """
model.properties = {} model.properties = {}
for name, property_json in json.get('properties', {}).items(): for name, property_json in json.get('properties', {}).items():
# TODO(calamity): support functions (callbacks) as properties. The model
# doesn't support it yet because the h/cc generators don't -- this is
# because we'd need to hook it into a base::Callback or something.
#
# However, pragmatically it's not necessary to support them anyway, since
# the instances of functions-on-properties in the extension APIs are all
# handled in pure Javascript on the render process (and .: never reach
# C++ let alone the browser).
if property_json.get('type') == 'function':
continue
model.properties[name] = Property( model.properties[name] = Property(
model, model,
name, name,
......
[
{
"namespace": "functions_as_parameters",
"types": [
{
"id": "FunctionType",
"type": "object",
"properties": {
"event_callback": {
"type": "function",
"parameters": { }
}
}
},
{
"id": "OptionalFunctionType",
"type": "object",
"properties": {
"event_callback": {
"type": "function",
"optional": true,
"parameters": { }
}
}
}
]
}
]
// 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/test/functions_as_parameters.h"
#include "testing/gtest/include/gtest/gtest.h"
using namespace test::api::functions_as_parameters;
TEST(JsonSchemaCompilerFunctionsAsParametersTest, PopulateRequiredFunction) {
// The expectation is that if any value is set for the function, then
// the function is "present".
{
DictionaryValue empty_value;
FunctionType out;
EXPECT_FALSE(FunctionType::Populate(empty_value, &out));
}
{
DictionaryValue value;
DictionaryValue function_dict;
value.Set("event_callback", function_dict.DeepCopy());
FunctionType out;
ASSERT_TRUE(FunctionType::Populate(value, &out));
EXPECT_TRUE(out.event_callback.empty());
}
}
TEST(JsonSchemaCompilerFunctionsAsParametersTest, RequiredFunctionToValue) {
{
DictionaryValue value;
DictionaryValue function_dict;
value.Set("event_callback", function_dict.DeepCopy());
FunctionType out;
ASSERT_TRUE(FunctionType::Populate(value, &out));
EXPECT_TRUE(value.Equals(out.ToValue().get()));
}
{
DictionaryValue value;
DictionaryValue expected_value;
DictionaryValue function_dict;
value.Set("event_callback", function_dict.DeepCopy());
expected_value.Set("event_callback", function_dict.DeepCopy());
FunctionType out;
ASSERT_TRUE(FunctionType::Populate(value, &out));
EXPECT_TRUE(expected_value.Equals(out.ToValue().get()));
}
}
TEST(JsonSchemaCompilerFunctionsAsParametersTest, PopulateOptionalFunction) {
{
DictionaryValue empty_value;
OptionalFunctionType out;
ASSERT_TRUE(OptionalFunctionType::Populate(empty_value, &out));
EXPECT_FALSE(out.event_callback.get());
}
{
DictionaryValue value;
DictionaryValue function_value;
value.Set("event_callback", function_value.DeepCopy());
OptionalFunctionType out;
ASSERT_TRUE(OptionalFunctionType::Populate(value, &out));
EXPECT_TRUE(out.event_callback.get());
}
{
DictionaryValue value;
DictionaryValue function_value;
value.Set("event_callback", function_value.DeepCopy());
OptionalFunctionType out;
ASSERT_TRUE(OptionalFunctionType::Populate(value, &out));
EXPECT_TRUE(out.event_callback.get());
}
}
TEST(JsonSchemaCompilerFunctionsAsParametersTest, OptionalFunctionToValue) {
{
DictionaryValue empty_value;
OptionalFunctionType out;
ASSERT_TRUE(OptionalFunctionType::Populate(empty_value, &out));
// event_callback should not be set in the return from ToValue.
EXPECT_TRUE(empty_value.Equals(out.ToValue().get()));
}
{
DictionaryValue value;
DictionaryValue function_value;
value.Set("event_callback", function_value.DeepCopy());
OptionalFunctionType out;
ASSERT_TRUE(OptionalFunctionType::Populate(value, &out));
EXPECT_TRUE(value.Equals(out.ToValue().get()));
}
}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
'choices.json', 'choices.json',
'crossref.json', 'crossref.json',
'enums.json', 'enums.json',
'functions_as_parameters.json',
'functions_on_types.json', 'functions_on_types.json',
'objects.json', 'objects.json',
'simple_api.json', 'simple_api.json',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment