Commit 89891098 authored by Hiroki Nakagawa's avatar Hiroki Nakagawa Committed by Commit Bot

Revert "JsonSchemaCompiler: Raise error on parse failures of optional properties."

This reverts commit eec7bff8.

Reason for revert:
After this change, JsonSchemaCompilerErrorTest.* are failing on
several linux bots:
https://ci.chromium.org/p/chromium/builders/ci/Linux%20Tests/95122
https://ci.chromium.org/p/chromium/builders/ci/Linux%20Tests%20%28dbg%29%281%29/92709
https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Tests/21165

Original change's description:
> JsonSchemaCompiler: Raise error on parse failures of optional properties.
>
> Currently when an optional property fails to parse and error generation
> is enabled for the schema, the auto-generated Populate function
> continues parsing the type but still populates error. This CL changes
> the code to ensure that we raise an error in these cases.
>
> We also don't raise an error on unrecognized keys any longer since we
> shouldn't be raising an error while returning a parse success: Most
> consumers currently don't distinguish between the two cases and this can
> lead to the returned error being a concatenation of unrelated errors,
> which is not ideal.
>
> We now enforce the constraint that Populate only returns an error iff
> there is a parse failure. We DCHECK the same in the auto-generated
> FromValue function.
>
> This also brings the code more in-line with what we do for
> auto-generated manifest parsing.
>
> Currently only 3 schemas use error generation:
>   - declarative_net_request.idl
>   - extensions_manifest_types.json
>   - manifest_types.json
>
> The latter two may be affected by the change. In particular, parsing
> manifest types declared by them may lead to a hard error when an
> optional property can't be parsed now.
>
> BUG=1113513
>
> Change-Id: I63966389e25f7591b4425815d32d9da59d35c3fb
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2500425
> Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
> Reviewed-by: Devlin <rdevlin.cronin@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#823545}

TBR=rdevlin.cronin@chromium.org,karandeepb@chromium.org

Change-Id: I3b26905381996192b377f79abcde757efaf08f31
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1113513
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2519330Reviewed-by: default avatarHiroki Nakagawa <nhiroki@chromium.org>
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823902}
parent b1080ddf
...@@ -673,17 +673,12 @@ TEST_P(SingleRulesetTest, InvalidJSONRules_Parsed) { ...@@ -673,17 +673,12 @@ TEST_P(SingleRulesetTest, InvalidJSONRules_Parsed) {
SetRules(base::JSONReader::ReadDeprecated(kRules)); SetRules(base::JSONReader::ReadDeprecated(kRules));
extension_loader()->set_ignore_manifest_warnings(true); extension_loader()->set_ignore_manifest_warnings(true);
LoadAndExpectSuccess(1u);
// Rules with ids "2" and "3" will be successfully indexed. Note that for rule
// with id "3", the "invalidKey" will simply be ignored during parsing
// (without causing any install warning).
size_t expected_rule_count = 2;
LoadAndExpectSuccess(expected_rule_count);
// TODO(crbug.com/879355): CrxInstaller reloads the extension after moving it, // TODO(crbug.com/879355): CrxInstaller reloads the extension after moving it,
// which causes it to lose the install warning. This should be fixed. // which causes it to lose the install warning. This should be fixed.
if (GetParam() != ExtensionLoadType::PACKED) { if (GetParam() != ExtensionLoadType::PACKED) {
ASSERT_EQ(2u, extension()->install_warnings().size()); ASSERT_EQ(3u, extension()->install_warnings().size());
std::vector<InstallWarning> expected_warnings; std::vector<InstallWarning> expected_warnings;
expected_warnings.emplace_back( expected_warnings.emplace_back(
...@@ -692,6 +687,12 @@ TEST_P(SingleRulesetTest, InvalidJSONRules_Parsed) { ...@@ -692,6 +687,12 @@ TEST_P(SingleRulesetTest, InvalidJSONRules_Parsed) {
"'condition': expected dictionary, got list"), "'condition': expected dictionary, got list"),
dnr_api::ManifestKeys::kDeclarativeNetRequest, dnr_api::ManifestKeys::kDeclarativeNetRequest,
dnr_api::DNRInfo::kRuleResources); dnr_api::DNRInfo::kRuleResources);
expected_warnings.emplace_back(
ErrorUtils::FormatErrorMessage(
GetErrorWithFilename(kRuleNotParsedWarning), "id 3",
"found unexpected key 'invalidKey'"),
dnr_api::ManifestKeys::kDeclarativeNetRequest,
dnr_api::DNRInfo::kRuleResources);
expected_warnings.emplace_back( expected_warnings.emplace_back(
ErrorUtils::FormatErrorMessage( ErrorUtils::FormatErrorMessage(
GetErrorWithFilename(kRuleNotParsedWarning), "index 4", GetErrorWithFilename(kRuleNotParsedWarning), "index 4",
......
...@@ -148,8 +148,8 @@ ReadJSONRulesResult ParseRulesFromJSON(const RulesetID& ruleset_id, ...@@ -148,8 +148,8 @@ ReadJSONRulesResult ParseRulesFromJSON(const RulesetID& ruleset_id,
dnr_api::Rule parsed_rule; dnr_api::Rule parsed_rule;
base::string16 parse_error; base::string16 parse_error;
if (dnr_api::Rule::Populate(rules_list[i], &parsed_rule, &parse_error)) { if (dnr_api::Rule::Populate(rules_list[i], &parsed_rule, &parse_error) &&
DCHECK(parse_error.empty()); parse_error.empty()) {
if (result.rules.size() == rule_limit) { if (result.rules.size() == rule_limit) {
result.rule_parse_warnings.push_back( result.rule_parse_warnings.push_back(
CreateInstallWarning(json_path, kRuleCountExceeded)); CreateInstallWarning(json_path, kRuleCountExceeded));
......
...@@ -125,10 +125,16 @@ std::unique_ptr<OptionsPageInfo> OptionsPageInfo::Create( ...@@ -125,10 +125,16 @@ std::unique_ptr<OptionsPageInfo> OptionsPageInfo::Create(
std::unique_ptr<OptionsUI> options_ui = std::unique_ptr<OptionsUI> options_ui =
OptionsUI::FromValue(*options_ui_value, &options_ui_error); OptionsUI::FromValue(*options_ui_value, &options_ui_error);
if (!options_ui) { if (!options_ui_error.empty()) {
// OptionsUI::FromValue populates |error| both when there are
// errors (in which case |options_ui| will be NULL) and warnings
// (in which case |options_ui| will be valid). Either way, show it
// as an install warning.
install_warnings->push_back( install_warnings->push_back(
InstallWarning(base::UTF16ToASCII(options_ui_error))); InstallWarning(base::UTF16ToASCII(options_ui_error)));
} else { }
if (options_ui) {
base::string16 options_parse_error; base::string16 options_parse_error;
if (!ParseOptionsUrl(extension, if (!ParseOptionsUrl(extension,
options_ui->page, options_ui->page,
......
...@@ -43,7 +43,6 @@ class _Generator(object): ...@@ -43,7 +43,6 @@ class _Generator(object):
.Append() .Append()
.Append(self._util_cc_helper.GetIncludePath()) .Append(self._util_cc_helper.GetIncludePath())
.Append('#include "base/check.h"') .Append('#include "base/check.h"')
.Append('#include "base/check_op.h"')
.Append('#include "base/notreached.h"') .Append('#include "base/notreached.h"')
.Append('#include "base/strings/string_number_conversions.h"') .Append('#include "base/strings/string_number_conversions.h"')
.Append('#include "base/strings/utf_string_conversions.h"') .Append('#include "base/strings/utf_string_conversions.h"')
...@@ -256,15 +255,24 @@ class _Generator(object): ...@@ -256,15 +255,24 @@ class _Generator(object):
if type_.properties or type_.additional_properties is not None: if type_.properties or type_.additional_properties is not None:
c.Append('const auto* dict = ' c.Append('const auto* dict = '
'static_cast<const base::DictionaryValue*>(&value);') 'static_cast<const base::DictionaryValue*>(&value);')
if self._generate_error_messages:
# TODO(crbug.com/1145154): The generated code here will ignore c.Append('std::set<std::string> keys;')
# unrecognized keys, but the parsing code for types passed to APIs in the
# renderer will hard-error on them. We should probably be consistent with
# the renderer here (at least for types also parsed in the renderer).
for prop in type_.properties.values(): for prop in type_.properties.values():
c.Concat(self._InitializePropertyToDefault(prop, 'out')) c.Concat(self._InitializePropertyToDefault(prop, 'out'))
for prop in type_.properties.values(): for prop in type_.properties.values():
if self._generate_error_messages:
c.Append('keys.insert("%s");' % (prop.name))
c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out')) c.Concat(self._GenerateTypePopulateProperty(prop, 'dict', 'out'))
# Check for extra values.
if self._generate_error_messages:
(c.Sblock('for (base::DictionaryValue::Iterator it(*dict); '
'!it.IsAtEnd(); it.Advance()) {')
.Sblock('if (!keys.count(it.key())) {')
.Concat(self._GenerateError('"found unexpected key \'" + '
'it.key() + "\'"'))
.Eblock('}')
.Eblock('}')
)
if type_.additional_properties is not None: if type_.additional_properties is not None:
if type_.additional_properties.property_type == PropertyType.ANY: if type_.additional_properties.property_type == PropertyType.ANY:
c.Append('out->additional_properties.MergeDictionary(dict);') c.Append('out->additional_properties.MergeDictionary(dict);')
...@@ -345,18 +353,15 @@ class _Generator(object): ...@@ -345,18 +353,15 @@ class _Generator(object):
.Append('std::unique_ptr<%s> %s::FromValue(%s) {' % (classname, .Append('std::unique_ptr<%s> %s::FromValue(%s) {' % (classname,
cpp_namespace, self._GenerateParams(('const base::Value& value',)))) cpp_namespace, self._GenerateParams(('const base::Value& value',))))
) )
c.Sblock();
if self._generate_error_messages: if self._generate_error_messages:
c.Append('DCHECK(error);') c.Append('DCHECK(error);')
c.Append('auto out = std::make_unique<%s>();' % classname) (c.Append(' auto out = std::make_unique<%s>();' % classname)
c.Append('bool result = Populate(%s);' % .Append(' if (!Populate(%s))' % self._GenerateArgs(
self._GenerateArgs(('value', 'out.get()'))) ('value', 'out.get()')))
if self._generate_error_messages: .Append(' return nullptr;')
c.Append('DCHECK_EQ(result, error->empty());') .Append(' return out;')
c.Sblock('if (!result)') .Append('}')
c.Append('return nullptr;') )
c.Eblock('return out;')
c.Eblock('}')
return c return c
def _GenerateTypeToValue(self, cpp_namespace, type_): def _GenerateTypeToValue(self, cpp_namespace, type_):
...@@ -916,7 +921,8 @@ class _Generator(object): ...@@ -916,7 +921,8 @@ class _Generator(object):
self._util_cc_helper.GetValueTypeString( self._util_cc_helper.GetValueTypeString(
'%%(src_var)s', True))))) '%%(src_var)s', True)))))
c.Append('%(dst_var)s.reset();') c.Append('%(dst_var)s.reset();')
c.Append('return %(failure_value)s;') if not self._generate_error_messages:
c.Append('return %(failure_value)s;')
(c.Eblock('}') (c.Eblock('}')
.Append('else') .Append('else')
.Append(' %(dst_var)s = std::make_unique<%(cpp_type)s>(temp);') .Append(' %(dst_var)s = std::make_unique<%(cpp_type)s>(temp);')
...@@ -940,9 +946,12 @@ class _Generator(object): ...@@ -940,9 +946,12 @@ class _Generator(object):
.Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {') .Sblock('if (!%(src_var)s->GetAsDictionary(&dictionary)) {')
.Concat(self._GenerateError( .Concat(self._GenerateError(
'"\'%%(key)s\': expected dictionary, got " + ' + '"\'%%(key)s\': expected dictionary, got " + ' +
self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))))
.Append('return %(failure_value)s;') # If an optional property fails to populate, the population can still
) # succeed with a warning. If no error messages are generated, this
# warning is not set and we fail out instead.
if not self._generate_error_messages:
c.Append('return %(failure_value)s;')
(c.Eblock('}') (c.Eblock('}')
.Sblock('else {') .Sblock('else {')
.Append('auto temp = std::make_unique<%(cpp_type)s>();') .Append('auto temp = std::make_unique<%(cpp_type)s>();')
...@@ -977,11 +986,14 @@ class _Generator(object): ...@@ -977,11 +986,14 @@ class _Generator(object):
# util_cc_helper deals with optional and required arrays # util_cc_helper deals with optional and required arrays
(c.Append('const base::ListValue* list = nullptr;') (c.Append('const base::ListValue* list = nullptr;')
.Sblock('if (!%(src_var)s->GetAsList(&list)) {') .Sblock('if (!%(src_var)s->GetAsList(&list)) {')
.Concat(self._GenerateError( .Concat(self._GenerateError(
'"\'%%(key)s\': expected list, got " + ' + '"\'%%(key)s\': expected list, got " + ' +
self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
.Append('return %(failure_value)s;')
) )
if is_ptr and self._generate_error_messages:
c.Append('%(dst_var)s.reset();')
else:
c.Append('return %(failure_value)s;')
c.Eblock('}') c.Eblock('}')
c.Sblock('else {') c.Sblock('else {')
item_type = self._type_helper.FollowRef(underlying_type.item_type) item_type = self._type_helper.FollowRef(underlying_type.item_type)
...@@ -998,7 +1010,10 @@ class _Generator(object): ...@@ -998,7 +1010,10 @@ class _Generator(object):
self._GenerateArgs(('*list', '&%(dst_var)s')))) self._GenerateArgs(('*list', '&%(dst_var)s'))))
c.Concat(self._GenerateError( c.Concat(self._GenerateError(
'"unable to populate array \'%%(parent_key)s\'"')) '"unable to populate array \'%%(parent_key)s\'"'))
c.Append('return %(failure_value)s;') if is_ptr and self._generate_error_messages:
c.Append('%(dst_var)s.reset();')
else:
c.Append('return %(failure_value)s;')
c.Eblock('}') c.Eblock('}')
c.Eblock('}') c.Eblock('}')
elif underlying_type.property_type == PropertyType.CHOICES: elif underlying_type.property_type == PropertyType.CHOICES:
...@@ -1023,8 +1038,9 @@ class _Generator(object): ...@@ -1023,8 +1038,9 @@ class _Generator(object):
.Concat(self._GenerateError( .Concat(self._GenerateError(
'"\'%%(key)s\': expected binary, got " + ' + '"\'%%(key)s\': expected binary, got " + ' +
self._util_cc_helper.GetValueTypeString('%%(src_var)s', True))) self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
.Append('return %(failure_value)s;')
) )
if not self._generate_error_messages:
c.Append('return %(failure_value)s;')
(c.Eblock('}') (c.Eblock('}')
.Sblock('else {') .Sblock('else {')
) )
......
...@@ -156,7 +156,7 @@ TEST(JsonSchemaCompilerErrorTest, WrongTypeValueType) { ...@@ -156,7 +156,7 @@ TEST(JsonSchemaCompilerErrorTest, WrongTypeValueType) {
Dictionary("otherType", std::make_unique<Value>(1.1)); Dictionary("otherType", std::make_unique<Value>(1.1));
errors::ObjectType out; errors::ObjectType out;
base::string16 error; base::string16 error;
EXPECT_FALSE(errors::ObjectType::Populate(*value, &out, &error)); EXPECT_TRUE(errors::ObjectType::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'otherType': expected dictionary, got double", EXPECT_TRUE(EqualsUtf16("'otherType': expected dictionary, got double",
error)); error));
EXPECT_EQ(NULL, out.other_type.get()); EXPECT_EQ(NULL, out.other_type.get());
...@@ -226,7 +226,9 @@ TEST(JsonSchemaCompilerErrorTest, BadEnumValue) { ...@@ -226,7 +226,9 @@ TEST(JsonSchemaCompilerErrorTest, BadEnumValue) {
} }
} }
TEST(JsonSchemaCompilerErrorTest, ErrorOnOptionalFailure) { // Warn but don't fail out errors
TEST(JsonSchemaCompilerErrorTest, WarnOnOptionalFailure) {
{ {
std::unique_ptr<base::DictionaryValue> value = std::unique_ptr<base::DictionaryValue> value =
Dictionary("string", std::make_unique<Value>("bling")); Dictionary("string", std::make_unique<Value>("bling"));
...@@ -239,7 +241,7 @@ TEST(JsonSchemaCompilerErrorTest, ErrorOnOptionalFailure) { ...@@ -239,7 +241,7 @@ TEST(JsonSchemaCompilerErrorTest, ErrorOnOptionalFailure) {
errors::OptionalTestType out; errors::OptionalTestType out;
base::string16 error; base::string16 error;
EXPECT_FALSE(errors::OptionalTestType::Populate(*value, &out, &error)); EXPECT_TRUE(errors::OptionalTestType::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'string': expected string, got integer", EXPECT_TRUE(EqualsUtf16("'string': expected string, got integer",
error)); error));
EXPECT_EQ(NULL, out.string.get()); EXPECT_EQ(NULL, out.string.get());
...@@ -260,7 +262,7 @@ TEST(JsonSchemaCompilerErrorTest, OptionalBinaryTypeFailure) { ...@@ -260,7 +262,7 @@ TEST(JsonSchemaCompilerErrorTest, OptionalBinaryTypeFailure) {
errors::OptionalBinaryData out; errors::OptionalBinaryData out;
base::string16 error; base::string16 error;
EXPECT_FALSE(errors::OptionalBinaryData::Populate(*value, &out, &error)); EXPECT_TRUE(errors::OptionalBinaryData::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'data': expected binary, got integer", EXPECT_TRUE(EqualsUtf16("'data': expected binary, got integer",
error)); error));
EXPECT_EQ(NULL, out.data.get()); EXPECT_EQ(NULL, out.data.get());
...@@ -278,7 +280,7 @@ TEST(JsonSchemaCompilerErrorTest, OptionalArrayTypeFailure) { ...@@ -278,7 +280,7 @@ TEST(JsonSchemaCompilerErrorTest, OptionalArrayTypeFailure) {
Dictionary("TheArray", std::make_unique<Value>(5)); Dictionary("TheArray", std::make_unique<Value>(5));
errors::ArrayObject out; errors::ArrayObject out;
base::string16 error; base::string16 error;
EXPECT_FALSE(errors::ArrayObject::Populate(*value, &out, &error)); EXPECT_TRUE(errors::ArrayObject::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer", EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer",
error)); error));
EXPECT_EQ(NULL, out.the_array.get()); EXPECT_EQ(NULL, out.the_array.get());
...@@ -298,8 +300,8 @@ TEST(JsonSchemaCompilerErrorTest, OptionalUnableToPopulateArray) { ...@@ -298,8 +300,8 @@ TEST(JsonSchemaCompilerErrorTest, OptionalUnableToPopulateArray) {
List(std::make_unique<Value>(5), std::make_unique<Value>(false)); List(std::make_unique<Value>(5), std::make_unique<Value>(false));
errors::OptionalChoiceType::Integers out; errors::OptionalChoiceType::Integers out;
base::string16 error; base::string16 error;
EXPECT_FALSE(errors::OptionalChoiceType::Integers::Populate(*params_value, EXPECT_TRUE(errors::OptionalChoiceType::Integers::Populate(*params_value,
&out, &error)); &out, &error));
EXPECT_TRUE(EqualsUtf16( EXPECT_TRUE(EqualsUtf16(
"expected integer, got boolean; unable to populate array 'integers'", "expected integer, got boolean; unable to populate array 'integers'",
error)); error));
...@@ -313,12 +315,12 @@ TEST(JsonSchemaCompilerErrorTest, MultiplePopulationErrors) { ...@@ -313,12 +315,12 @@ TEST(JsonSchemaCompilerErrorTest, MultiplePopulationErrors) {
Dictionary("TheArray", std::make_unique<Value>(5)); Dictionary("TheArray", std::make_unique<Value>(5));
errors::ArrayObject out; errors::ArrayObject out;
base::string16 error; base::string16 error;
EXPECT_FALSE(errors::ArrayObject::Populate(*value, &out, &error)); EXPECT_TRUE(errors::ArrayObject::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer", EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer",
error)); error));
EXPECT_EQ(NULL, out.the_array.get()); EXPECT_EQ(NULL, out.the_array.get());
EXPECT_FALSE(errors::ArrayObject::Populate(*value, &out, &error)); EXPECT_TRUE(errors::ArrayObject::Populate(*value, &out, &error));
EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer; " EXPECT_TRUE(EqualsUtf16("'TheArray': expected list, got integer; "
"'TheArray': expected list, got integer", "'TheArray': expected list, got integer",
error)); error));
...@@ -333,10 +335,10 @@ TEST(JsonSchemaCompilerErrorTest, TooManyKeys) { ...@@ -333,10 +335,10 @@ TEST(JsonSchemaCompilerErrorTest, TooManyKeys) {
EXPECT_TRUE(EqualsUtf16("", GetPopulateError<errors::TestType>(*value))); EXPECT_TRUE(EqualsUtf16("", GetPopulateError<errors::TestType>(*value)));
} }
{ {
// We simply ignore extra keys.
std::unique_ptr<base::DictionaryValue> value = std::unique_ptr<base::DictionaryValue> value =
Dictionary("string", std::make_unique<Value>("yes"), "ohno", Dictionary("string", std::make_unique<Value>("yes"), "ohno",
std::make_unique<Value>("many values")); std::make_unique<Value>("many values"));
EXPECT_TRUE(EqualsUtf16("", GetPopulateError<errors::TestType>(*value))); EXPECT_TRUE(EqualsUtf16("found unexpected key 'ohno'",
GetPopulateError<errors::TestType>(*value)));
} }
} }
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