JSON Parser allows union type fields to come after unions.

This is useful because many JSON generators will sort the fields,
cause X_type to follow X.

Change-Id: I00ef3ac05418224fc05aee93e6b3b3597e73ffe3
Tested: on Linux.
Bug: 29221752
This commit is contained in:
Wouter van Oortmerssen
2016-06-20 16:06:30 -07:00
parent 3639032d1e
commit 9e6c5f9f2c
5 changed files with 80 additions and 30 deletions

View File

@@ -583,9 +583,9 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
FieldDef *typefield = nullptr;
if (type.base_type == BASE_TYPE_UNION) {
// For union fields, add a second auto-generated field to hold the type,
// with _type appended as the name.
ECHECK(AddField(struct_def, name + "_type", type.enum_def->underlying_type,
&typefield));
// with a special suffix.
ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(),
type.enum_def->underlying_type, &typefield));
}
FieldDef *field;
@@ -686,17 +686,45 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
}
CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
size_t parent_fieldn) {
size_t parent_fieldn,
const StructDef *parent_struct_def) {
switch (val.type.base_type) {
case BASE_TYPE_UNION: {
assert(field);
std::string constant;
if (!parent_fieldn ||
field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE)
return Error("missing type field before this union value: " +
field->name);
field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE) {
// 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.
auto type_name = field->name + UnionTypeFieldSuffix();
assert(parent_struct_def);
auto type_field = parent_struct_def->fields.Lookup(type_name);
assert(type_field); // Guaranteed by ParseField().
// Remember where we are in the source file, so we can come back here.
auto backup = *static_cast<ParserState *>(this);
ECHECK(SkipAnyJsonValue()); // The table.
EXPECT(',');
auto next_name = attribute_;
if (Is(kTokenStringConstant)) {
NEXT();
} else {
EXPECT(kTokenIdentifier);
}
if (next_name != type_name)
return Error("missing type field after this union value: " +
type_name);
EXPECT(':');
Value type_val = type_field->value;
ECHECK(ParseAnyValue(type_val, type_field, 0, nullptr));
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(field_stack_.back().first.constant.c_str(), *this,
&enum_idx));
ECHECK(atot(constant.c_str(), *this, &enum_idx));
auto enum_val = val.type.enum_def->ReverseLookup(enum_idx);
if (!enum_val) return Error("illegal type id for: " + field->name);
ECHECK(ParseTable(*enum_val->struct_def, &val.constant, nullptr));
@@ -771,7 +799,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
NEXT(); // Ignore this field.
} else {
Value val = field->value;
ECHECK(ParseAnyValue(val, field, fieldn));
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.
@@ -870,7 +898,7 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) {
if ((!opts.strict_json || !count) && Is(']')) { NEXT(); break; }
Value val;
val.type = type;
ECHECK(ParseAnyValue(val, nullptr, 0));
ECHECK(ParseAnyValue(val, nullptr, 0, nullptr));
field_stack_.push_back(std::make_pair(val, nullptr));
count++;
if (Is(']')) { NEXT(); break; }
@@ -1324,7 +1352,8 @@ CheckedError Parser::ParseDecl() {
}
}
ECHECK(CheckClash(fields, struct_def, "_type", BASE_TYPE_UNION));
ECHECK(CheckClash(fields, struct_def, UnionTypeFieldSuffix(),
BASE_TYPE_UNION));
ECHECK(CheckClash(fields, struct_def, "Type", BASE_TYPE_UNION));
ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_VECTOR));
ECHECK(CheckClash(fields, struct_def, "Length", BASE_TYPE_VECTOR));