Fixed unions not being parsed correctly in JSON.

This would happen if they were supplied in an order that does not match
the schema relative to other fields. It now supports any ordering.

Change-Id: I9d309cd4e6e5c470f01d9d431806eba4f9f46559
Tested: on Linux.
This commit is contained in:
Wouter van Oortmerssen
2017-01-18 12:02:31 -08:00
parent 19101826a8
commit eac2905568

View File

@@ -722,8 +722,17 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
case BASE_TYPE_UNION: {
assert(field);
std::string constant;
if (!parent_fieldn ||
field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE) {
// Find corresponding type field we may have already parsed.
for (auto elem = field_stack_.rbegin();
elem != field_stack_.rbegin() + parent_fieldn; ++elem) {
auto &type = elem->second->value.type;
if (type.base_type == BASE_TYPE_UTYPE &&
type.enum_def == val.type.enum_def) {
constant = elem->first.constant;
break;
}
}
if (constant.empty()) {
// We haven't seen the type field yet. Sadly a lot of JSON writers
// output these in alphabetical order, meaning it comes after this
// value. So we scan past the value to find it, then come back here.
@@ -750,8 +759,6 @@ CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
constant = type_val.constant;
// Got the information we needed, now rewind:
*static_cast<ParserState *>(this) = backup;
} else {
constant = field_stack_.back().first.constant;
}
uint8_t enum_idx;
ECHECK(atot(constant.c_str(), *this, &enum_idx));
@@ -830,16 +837,18 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
} else {
Value val = field->value;
ECHECK(ParseAnyValue(val, field, fieldn, &struct_def));
size_t i = field_stack_.size();
// Hardcoded insertion-sort with error-check.
// If fields are specified in order, then this loop exits immediately.
for (; i > field_stack_.size() - fieldn; i--) {
auto existing_field = field_stack_[i - 1].second;
auto elem = field_stack_.rbegin();
for (; elem != field_stack_.rbegin() + fieldn; ++elem) {
auto existing_field = elem->second;
if (existing_field == field)
return Error("field set more than once: " + field->name);
if (existing_field->value.offset < field->value.offset) break;
}
field_stack_.insert(field_stack_.begin() + i, std::make_pair(val, field));
// Note: elem points to before the insertion point, thus .base() points
// to the correct spot.
field_stack_.insert(elem.base(), std::make_pair(val, field));
fieldn++;
}
}