From 4623cfa4bc32eb604e8a9ac31a1ec94f8859abc9 Mon Sep 17 00:00:00 2001 From: souma987 <64578761+souma987@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:54:08 +0900 Subject: [PATCH 1/2] Opt in to using experimental Kotlin Native APIs to suppress build warnings (#8885) - fixes #8846 --- .../kotlin/com/google/flatbuffers/kotlin/ByteArray.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt b/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt index 21cc45af4..4069b4792 100644 --- a/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt +++ b/kotlin/flatbuffers-kotlin/src/nativeMain/kotlin/com/google/flatbuffers/kotlin/ByteArray.kt @@ -14,9 +14,12 @@ * limitations under the License. */ @file:Suppress("NOTHING_TO_INLINE") +@file:OptIn(ExperimentalNativeApi::class) package com.google.flatbuffers.kotlin +import kotlin.experimental.ExperimentalNativeApi + /** * This implementation assumes that of native macOSX64 the byte order of the implementation is * Little Endian. From ea0a73d1683c50821867edcaf2d358b641b68ce9 Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 4 Feb 2026 10:15:05 -0500 Subject: [PATCH 2/2] disallow circular struct references (#8851) * detect and fail for circular struct dependencies * pr comments * pr comment --- include/flatbuffers/idl.h | 13 ++++++++++- src/flatc.cpp | 3 +++ src/idl_parser.cpp | 36 +++++++++++++++++++++++++++++++ tests/flatc/flatc_schema_tests.py | 9 ++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 06f93f338..9e58d4be9 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -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 original_location; std::vector reserved_ids; }; @@ -1101,6 +1110,8 @@ class Parser : public ParserState { // others includes. std::vector GetIncludedFiles() const; + bool HasCircularStructDependency(); + private: class ParseDepthGuard; diff --git a/src/flatc.cpp b/src/flatc.cpp index 6a5708574..ee34044fd 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -927,6 +927,9 @@ std::unique_ptr 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(); } diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 0aaccbb2c..569c76762 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2755,6 +2755,42 @@ std::vector Parser::GetIncludedFiles() const { return {it->second.cbegin(), it->second.cend()}; } +bool Parser::HasCircularStructDependency() { + std::function 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 | diff --git a/tests/flatc/flatc_schema_tests.py b/tests/flatc/flatc_schema_tests.py index ebd5a7916..f91520874 100644 --- a/tests/flatc/flatc_schema_tests.py +++ b/tests/flatc/flatc_schema_tests.py @@ -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"]) \ No newline at end of file