forked from BigfootDev/flatbuffers
disallow circular struct references (#8851)
* detect and fail for circular struct dependencies * pr comments * pr comment
This commit is contained in:
@@ -395,13 +395,20 @@ struct FieldDef : public Definition {
|
||||
};
|
||||
|
||||
struct StructDef : public Definition {
|
||||
enum class CycleStatus {
|
||||
NotChecked,
|
||||
InProgress,
|
||||
Checked,
|
||||
};
|
||||
|
||||
StructDef()
|
||||
: fixed(false),
|
||||
predecl(true),
|
||||
sortbysize(true),
|
||||
has_key(false),
|
||||
minalign(1),
|
||||
bytesize(0) {}
|
||||
bytesize(0),
|
||||
cycle_status{CycleStatus::NotChecked} {}
|
||||
|
||||
void PadLastField(size_t min_align) {
|
||||
auto padding = PaddingBytes(bytesize, min_align);
|
||||
@@ -423,6 +430,8 @@ struct StructDef : public Definition {
|
||||
size_t minalign; // What the whole object needs to be aligned to.
|
||||
size_t bytesize; // Size if fixed.
|
||||
|
||||
CycleStatus cycle_status; // used for determining if we have circular references
|
||||
|
||||
flatbuffers::unique_ptr<std::string> original_location;
|
||||
std::vector<voffset_t> reserved_ids;
|
||||
};
|
||||
@@ -1101,6 +1110,8 @@ class Parser : public ParserState {
|
||||
// others includes.
|
||||
std::vector<IncludedFile> GetIncludedFiles() const;
|
||||
|
||||
bool HasCircularStructDependency();
|
||||
|
||||
private:
|
||||
class ParseDepthGuard;
|
||||
|
||||
|
||||
@@ -927,6 +927,9 @@ std::unique_ptr<Parser> FlatCompiler::GenerateCode(const FlatCOptions& options,
|
||||
auto err = parser->ConformTo(conform_parser);
|
||||
if (!err.empty()) Error("schemas don\'t conform: " + err, false);
|
||||
}
|
||||
if (parser->HasCircularStructDependency()) {
|
||||
Error("schema has circular struct dependencies: " + filename, false);
|
||||
}
|
||||
if (options.schema_binary || opts.binary_schema_gen_embed) {
|
||||
parser->Serialize();
|
||||
}
|
||||
|
||||
@@ -2755,6 +2755,42 @@ std::vector<IncludedFile> Parser::GetIncludedFiles() const {
|
||||
return {it->second.cbegin(), it->second.cend()};
|
||||
}
|
||||
|
||||
bool Parser::HasCircularStructDependency() {
|
||||
std::function<bool(StructDef*)> visit =
|
||||
[&](StructDef* struct_def) {
|
||||
// Only consider fixed structs and structs we have yet to check
|
||||
if (!struct_def->fixed || struct_def->cycle_status == StructDef::CycleStatus::Checked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (struct_def->cycle_status == StructDef::CycleStatus::InProgress) {
|
||||
// cycle found
|
||||
return true;
|
||||
}
|
||||
|
||||
struct_def->cycle_status = StructDef::CycleStatus::InProgress;
|
||||
|
||||
for (const auto& field : struct_def->fields.vec) {
|
||||
if (field->value.type.base_type == BASE_TYPE_STRUCT) {
|
||||
if (visit(field->value.type.struct_def)) {
|
||||
return true; // Cycle detected in recursion.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct_def->cycle_status = StructDef::CycleStatus::Checked;
|
||||
return false; // No cycle detected.
|
||||
};
|
||||
|
||||
for (const auto& struct_def : structs_.vec) {
|
||||
if (visit(struct_def)) {
|
||||
return true; // Cycle detected.
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No cycle detected.
|
||||
}
|
||||
|
||||
bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions& opts) {
|
||||
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
|
||||
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster |
|
||||
|
||||
@@ -71,3 +71,12 @@ class SchemaTests:
|
||||
schema_json["enums"][0]["values"][2]["attributes"][1]["value"]
|
||||
== "Value 3 (deprecated)"
|
||||
)
|
||||
|
||||
def CircularStructDependency(self):
|
||||
try:
|
||||
flatc(["-c", "circular_struct_dependency.fbs"])
|
||||
assert False, "Expected flatc to fail on circular struct dependency"
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
flatc(["-c", "circular_table.fbs"])
|
||||
Reference in New Issue
Block a user