handle tagged unions in check-structures-sanity

update structures
develop
Ben Lubar 2020-02-20 22:17:25 -06:00
parent 903f829e30
commit 38410544b0
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
2 changed files with 101 additions and 11 deletions

@ -1 +1 @@
Subproject commit 2ade3d1f816d4fcae420094f060126453a6c5fe5
Subproject commit c1a4a9c4653c694050ac3298896696016a56b9c9

@ -93,13 +93,14 @@ private:
bool check_vtable(const ToCheck &, void *, type_identity *);
void queue_field(ToCheck &&, const struct_field_info *);
void queue_static_array(const ToCheck &, void *, type_identity *, size_t, bool = false, enum_identity * = nullptr);
bool maybe_queue_tagged_union(const ToCheck &, const struct_field_info *);
void check_dispatch(const ToCheck &);
void check_global(const ToCheck &);
void check_primitive(const ToCheck &);
void check_stl_string(const ToCheck &);
void check_pointer(const ToCheck &);
void check_bitfield(const ToCheck &);
void check_enum(const ToCheck &);
int64_t check_enum(const ToCheck &);
void check_container(const ToCheck &);
void check_vector(const ToCheck &, type_identity *, bool);
void check_deque(const ToCheck &, type_identity *);
@ -458,6 +459,87 @@ void Checker::queue_static_array(const ToCheck & array, void *base, type_identit
}
}
bool Checker::maybe_queue_tagged_union(const ToCheck & item, const struct_field_info *field)
{
const struct_field_info *tag_field = field + 1;
if (field->mode != struct_field_info::SUBSTRUCT || tag_field->mode != struct_field_info::PRIMITIVE)
{
return false;
}
if (!tag_field->type || tag_field->type->type() != IDTYPE_ENUM)
{
return false;
}
auto tag_identity = static_cast<enum_identity *>(tag_field->type);
if (!field->type || field->type->type() != IDTYPE_STRUCT)
{
return false;
}
auto union_identity = static_cast<struct_identity *>(field->type);
if (!union_identity->getFields() || union_identity->getFields()->mode != struct_field_info::POINTER)
{
return false;
}
for (auto union_member = union_identity->getFields(); union_member->mode != struct_field_info::END; union_member++)
{
// count = 2 means we're in a union
if (union_member->mode != struct_field_info::POINTER || union_member->count != 2 || union_member->offset)
{
return false;
}
}
// unsupported: tagged union with complex enum
if (tag_identity->getComplex())
{
return false;
}
// at this point, we're committed to it being a tagged union.
ToCheck tag_item(item, "." + std::string(tag_field->name), PTR_ADD(item.ptr, tag_field->offset), tag_field->type);
int64_t tag_value = check_enum(tag_item);
if (tag_value < tag_identity->getFirstItem() || tag_value > tag_identity->getLastItem())
{
FAIL("tagged union (" << field->name << ", " << tag_field->name << ") tag out of range (" << tag_value << ")");
return true;
}
const char *tag_key = tag_identity->getKeys()[tag_value - tag_identity->getFirstItem()];
if (!tag_key)
{
FAIL("tagged union (" << field->name << ", " << tag_field->name << ") tag unnamed (" << tag_value << ")");
return true;
}
const struct_field_info *union_field = nullptr;
for (auto union_member = union_identity->getFields(); union_member->mode != struct_field_info::END; union_member++)
{
if (!strcmp(tag_key, union_member->name))
{
union_field = union_member;
break;
}
}
if (!union_field)
{
FAIL("tagged union (" << field->name << ", " << tag_field->name << ") missing member for tag " << tag_key << " (" << tag_value << ")");
return true;
}
ToCheck tagged_union_item(item, stl_sprintf(".%s.%s", field->name, union_field->name), PTR_ADD(item.ptr, field->offset), union_field->type);
queue_field(std::move(tagged_union_item), union_field);
return true;
}
void Checker::check_dispatch(const ToCheck & item)
{
if (!item.identity)
@ -686,13 +768,8 @@ void Checker::check_bitfield(const ToCheck & item)
}
}
void Checker::check_enum(const ToCheck & item)
int64_t Checker::check_enum(const ToCheck & item)
{
if (!enums)
{
return;
}
auto identity = static_cast<enum_identity *>(item.identity);
int64_t value;
@ -721,7 +798,12 @@ void Checker::check_enum(const ToCheck & item)
break;
default:
UNEXPECTED;
return;
return -1;
}
if (!enums)
{
return value;
}
size_t index;
@ -731,7 +813,7 @@ void Checker::check_enum(const ToCheck & item)
if (it == cplx->value_index_map.cend())
{
FAIL("enum value (" << value << ") is not defined (complex enum)");
return;
return value;
}
index = it->second;
}
@ -740,7 +822,7 @@ void Checker::check_enum(const ToCheck & item)
if (value < identity->getFirstItem() || value > identity->getLastItem())
{
FAIL("enum value (" << value << ") outside of defined range (" << identity->getFirstItem() << " to " << identity->getLastItem() << ")");
return;
return value;
}
index = value - identity->getFirstItem();
}
@ -749,6 +831,8 @@ void Checker::check_enum(const ToCheck & item)
{
FAIL("enum value (" << value << ") is unnamed");
}
return value;
}
void Checker::check_container(const ToCheck & item)
@ -952,6 +1036,12 @@ void Checker::check_struct(const ToCheck & item)
for (auto field = fields; field->mode != struct_field_info::END; field++)
{
if (maybe_queue_tagged_union(item, field))
{
field++;
continue;
}
ToCheck child(item, std::string(".") + field->name, PTR_ADD(item.ptr, field->offset), field->type);
queue_field(std::move(child), field);