using System.Collections.Generic; namespace AyCode.Core.Serializers.SourceGenerator; // Source-generator model types — pure POCO data carriers describing a `[AcBinarySerializable]` type // and its serializable properties. Consumed by all emit / diagnostics / analysis passes in the partial // `AcBinarySourceGenerator` class (see siblings `*.GenWriter.cs`, `*.GenReader.cs`, etc.). internal sealed class SerializableClassInfo { public string Namespace { get; } public string ClassName { get; } public string FullTypeName { get; } public List Properties { get; } /// True if this type implements IId<T> public bool IsIId { get; } /// The Id type name ("int", "long", "System.Guid") if IsIId, null otherwise public string? IdTypeName { get; } /// True if EnableRefHandlingFeature is enabled — controls non-IId All mode tracking code emission. public bool EnableRefHandling { get; } /// FNV-1a hash of ClassName (matches runtime SourceType.Name hash) public int TypeNameHash { get; } /// FNV-1a hash of each property name, in property order public int[] PropertyNameHashes { get; } /// When false, skip inline metadata and use markerless property write for this type. public bool EnableMetadata { get; } /// True if EnablePropertyFilterFeature is enabled — controls per-property HasPropertyFilter /// guard emission in WriteProperties / ScanObject. When false, the filter check is omitted entirely /// → leaner generated code on the hot path (typical for high-throughput types that never use a filter). public bool EnablePropertyFilter { get; } /// True if EnablePolymorphDetectFeature is enabled — controls ObjectWithTypeName + AQN /// prefix emit on System.Object-declared properties. When false, the prefix is suppressed /// AND ACBIN002 fires at build time if such a property exists on this type (guarding against silent /// wire corruption). Opt-out is intentional: dev guarantees no polymorphic object property /// will be serialized on this type, or all such properties are excluded via [AcBinaryIgnore]. public bool EnablePolymorphDetect { get; } /// True if EnableInternStringFeature is enabled — controls whether the SGen-emitted reader /// contains StringInterned, StringInternFirstSmall, StringInternFirstMedium case-ágakat. /// When false, those cases are omitted (the writer doesn't emit those markers when intern is off, /// so the reader doesn't need to handle them). Leaner switch dispatch (~30% fewer string cases) + /// smaller IL → faster cold-start JIT + smaller AOT publish. public bool EnableInternString { get; } /// When true, type subtree has IId types needing scan (active in OnlyId + All). public bool NeedsIdScan { get; } /// When true, type subtree has non-IId ref tracking (active only in All mode). public bool NeedsAllRefScan { get; } /// When true, type subtree needs string interning scan. public bool NeedsInternScan { get; } /// Derived: NeedsIdScan || NeedsAllRefScan. public bool NeedsRefScan => NeedsIdScan || NeedsAllRefScan; /// Derived: any scan axis active. public bool NeedsScan => NeedsIdScan || NeedsAllRefScan || NeedsInternScan; public SerializableClassInfo(string ns, string cn, string ftn, List p, bool isIId, string? idTypeName, bool enableRefHandling, int typeNameHash, int[] propertyNameHashes, bool enableMetadata, bool enablePropertyFilter, bool enablePolymorphDetect, bool enableInternString, bool needsIdScan, bool needsAllRefScan, bool needsInternScan) { Namespace = ns; ClassName = cn; FullTypeName = ftn; Properties = p; IsIId = isIId; IdTypeName = idTypeName; EnableRefHandling = enableRefHandling; TypeNameHash = typeNameHash; PropertyNameHashes = propertyNameHashes; EnableMetadata = enableMetadata; EnablePropertyFilter = enablePropertyFilter; EnablePolymorphDetect = enablePolymorphDetect; EnableInternString = enableInternString; NeedsIdScan = needsIdScan; NeedsAllRefScan = needsAllRefScan; NeedsInternScan = needsInternScan; } } internal sealed class PropInfo { public string Name { get; } public string TypeName { get; } /// /// Type name safe for typeof() — nullable ref type annotation stripped (typeof(T?) invalid for ref types). /// public string TypeNameForTypeof { get; } public PropertyTypeKind TypeKind { get; } public bool IsNullable { get; } /// /// Pre-computed interning flags matching runtime BinaryPropertyAccessorBase._interningFlags. /// Bit layout: bit N = eligible when StringInterningMode == N. /// None=0 → bit 0 never set. Attribute=1 → bit 1. All=2 → bit 2. /// No attr: 0b100 (4), [AcStringIntern(true)]: 0b110 (6), [AcStringIntern(false)]: 0b000 (0). /// public int InterningFlags { get; } /// True when declared property type is System.Object. Runtime type dispatch needed. public bool IsObjectDeclaredType { get; } /// True if the Complex property type has [AcBinarySerializable] → has a generated writer. public bool HasGeneratedWriter { get; } /// True if the Complex property type implements IId<T> → needs ref tracking in write pass. public bool IsIId { get; } /// Generated writer class name, e.g. "SharedTag_GeneratedWriter". Only set when HasGeneratedWriter. public string? WriterClassName { get; } /// Id type name ("int", "long", "System.Guid") for IId child types. Null if not IId. public string? IdTypeName { get; } // Collection element metadata — set when TypeKind == Collection and element type is Complex with generated writer /// Element type kind for collection properties. Only meaningful when TypeKind == Collection. public PropertyTypeKind ElementKind { get; } /// True if collection element type has [AcBinarySerializable]. public bool ElementHasGeneratedWriter { get; } /// True if collection element type implements IId<T>. public bool ElementIsIId { get; } /// Generated writer class name for collection element type. public string? ElementWriterClassName { get; } /// Id type name for collection element IId types. Null if not IId. public string? ElementIdTypeName { get; } /// Collection type: "List", "Array", "IndexedCollection", "Counted", or null (unknown — fallback to runtime). public string? CollectionKind { get; } /// Full element type name for generated code (e.g. "SharedTag"). public string? ElementFullTypeName { get; } /// Add method for Counted concrete collections. null → List<T>.Add(), "Add" → HashSet/SortedSet, "Enqueue" → Queue, "AddLast" → LinkedList. public string? CollectionAddMethod { get; } /// True if the concrete Counted collection has a capacity constructor (HashSet, Queue). public bool CollectionHasCapacityCtor { get; } // Dictionary metadata — set when TypeKind == Dictionary /// Key type kind for dictionary properties. public PropertyTypeKind DictKeyKind { get; } /// Value type kind for dictionary properties. public PropertyTypeKind DictValueKind { get; } /// Key type name for generated code. public string? DictKeyTypeName { get; } /// Value type name for generated code. public string? DictValueTypeName { get; } /// True if dictionary value type has [AcBinarySerializable]. public bool DictValueHasGeneratedWriter { get; } /// Generated writer class name for dictionary value type. public string? DictValueWriterClassName { get; } /// True if dictionary value type implements IId<T>. public bool DictValueIsIId { get; } /// When false, dict value type skips inline metadata. public bool DictValueEnableMetadata { get; } /// FNV-1a hash of dict value type name. public int DictValueTypeNameHash { get; } /// FNV-1a hashes of dict value type's properties. public int[]? DictValuePropertyHashes { get; } /// When true, dict value subtree has IId types needing scan. public bool DictValueNeedsIdScan { get; } /// When true, dict value subtree has non-IId ref tracking. public bool DictValueNeedsAllRefScan { get; } /// When true, dict value subtree needs string interning scan. public bool DictValueNeedsInternScan { get; } /// Derived: DictValueNeedsIdScan || DictValueNeedsAllRefScan. public bool DictValueNeedsRefScan => DictValueNeedsIdScan || DictValueNeedsAllRefScan; /// Derived: any dict value scan axis active. public bool DictValueNeedsScan => DictValueNeedsIdScan || DictValueNeedsAllRefScan || DictValueNeedsInternScan; // UseMetadata inline hash-ek (Complex/Collection child típushoz) /// FNV-1a hash of child type name (Complex property). Only set when HasGeneratedWriter. public int ChildTypeNameHash { get; } /// FNV-1a hashes of child type's properties. Only set when HasGeneratedWriter. public int[]? ChildPropertyHashes { get; } /// FNV-1a hash of collection element type name. Only set when ElementHasGeneratedWriter. public int ElementTypeNameHash { get; } /// FNV-1a hashes of collection element type's properties. Only set when ElementHasGeneratedWriter. public int[]? ElementPropertyHashes { get; } /// When false, child Complex type skips inline metadata in generated code. public bool ChildEnableMetadata { get; } /// When false, collection element type skips inline metadata in generated code. public bool ElementEnableMetadata { get; } /// When true, child subtree has IId types needing scan (active in OnlyId + All). public bool ChildNeedsIdScan { get; } /// When true, child subtree has non-IId ref tracking (active only in All mode). public bool ChildNeedsAllRefScan { get; } /// When true, child subtree needs string interning scan. public bool ChildNeedsInternScan { get; } /// Derived: ChildNeedsIdScan || ChildNeedsAllRefScan. public bool ChildNeedsRefScan => ChildNeedsIdScan || ChildNeedsAllRefScan; /// Derived: any child scan axis active. public bool ChildNeedsScan => ChildNeedsIdScan || ChildNeedsAllRefScan || ChildNeedsInternScan; /// When true, element subtree has IId types needing scan (active in OnlyId + All). public bool ElementNeedsIdScan { get; } /// When true, element subtree has non-IId ref tracking (active only in All mode). public bool ElementNeedsAllRefScan { get; } /// When true, element subtree needs string interning scan. public bool ElementNeedsInternScan { get; } /// Derived: ElementNeedsIdScan || ElementNeedsAllRefScan. public bool ElementNeedsRefScan => ElementNeedsIdScan || ElementNeedsAllRefScan; /// Derived: any element scan axis active. public bool ElementNeedsScan => ElementNeedsIdScan || ElementNeedsAllRefScan || ElementNeedsInternScan; public PropInfo(string n, string tn, string tnForTypeof, PropertyTypeKind tk, bool nullable, bool isObjectDeclaredType = false, bool? stringInternAttr = null, bool hasGeneratedWriter = false, bool isIId = false, string? writerClassName = null, string? idTypeName = null, PropertyTypeKind elementKind = PropertyTypeKind.Unknown, bool elementHasGenWriter = false, bool elementIsIId = false, string? elementWriterClassName = null, string? elementIdTypeName = null, string? collectionKind = null, string? elementFullTypeName = null, string? collectionAddMethod = null, bool collectionHasCapacityCtor = false, PropertyTypeKind dictKeyKind = PropertyTypeKind.Unknown, PropertyTypeKind dictValueKind = PropertyTypeKind.Unknown, string? dictKeyTypeName = null, string? dictValueTypeName = null, bool dictValueHasGeneratedWriter = false, string? dictValueWriterClassName = null, bool dictValueIsIId = false, bool dictValueEnableMetadata = true, int dictValueTypeNameHash = 0, int[]? dictValuePropertyHashes = null, bool dictValueNeedsIdScan = true, bool dictValueNeedsAllRefScan = true, bool dictValueNeedsInternScan = true, int childTypeNameHash = 0, int[]? childPropertyHashes = null, int elementTypeNameHash = 0, int[]? elementPropertyHashes = null, bool childEnableMetadata = true, bool elementEnableMetadata = true, bool childNeedsIdScan = true, bool childNeedsAllRefScan = true, bool childNeedsInternScan = true, bool elementNeedsIdScan = true, bool elementNeedsAllRefScan = true, bool elementNeedsInternScan = true) { Name = n; TypeName = tn; TypeNameForTypeof = tnForTypeof; TypeKind = tk; IsNullable = nullable; IsObjectDeclaredType = isObjectDeclaredType; HasGeneratedWriter = hasGeneratedWriter; IsIId = isIId; WriterClassName = writerClassName; IdTypeName = idTypeName; ElementKind = elementKind; ElementHasGeneratedWriter = elementHasGenWriter; ElementIsIId = elementIsIId; ElementWriterClassName = elementWriterClassName; ElementIdTypeName = elementIdTypeName; CollectionKind = collectionKind; ElementFullTypeName = elementFullTypeName; CollectionAddMethod = collectionAddMethod; CollectionHasCapacityCtor = collectionHasCapacityCtor; DictKeyKind = dictKeyKind; DictValueKind = dictValueKind; DictKeyTypeName = dictKeyTypeName; DictValueTypeName = dictValueTypeName; DictValueHasGeneratedWriter = dictValueHasGeneratedWriter; DictValueWriterClassName = dictValueWriterClassName; DictValueIsIId = dictValueIsIId; DictValueEnableMetadata = dictValueEnableMetadata; DictValueTypeNameHash = dictValueTypeNameHash; DictValuePropertyHashes = dictValuePropertyHashes; DictValueNeedsIdScan = dictValueNeedsIdScan; DictValueNeedsAllRefScan = dictValueNeedsAllRefScan; DictValueNeedsInternScan = dictValueNeedsInternScan; ChildTypeNameHash = childTypeNameHash; ChildPropertyHashes = childPropertyHashes; ElementTypeNameHash = elementTypeNameHash; ElementPropertyHashes = elementPropertyHashes; ChildEnableMetadata = childEnableMetadata; ElementEnableMetadata = elementEnableMetadata; ChildNeedsIdScan = childNeedsIdScan; ChildNeedsAllRefScan = childNeedsAllRefScan; ChildNeedsInternScan = childNeedsInternScan; ElementNeedsIdScan = elementNeedsIdScan; ElementNeedsAllRefScan = elementNeedsAllRefScan; ElementNeedsInternScan = elementNeedsInternScan; // Mirror runtime _interningFlags computation from BinaryPropertyAccessorBase int flags = 0; if (stringInternAttr == true) flags |= (1 << 1); // Attribute bit if (stringInternAttr != false) flags |= (1 << 2); // All bit InterningFlags = flags; } } internal enum PropertyTypeKind { Unknown, String, Int32, Int64, Int16, Byte, UInt16, UInt32, UInt64, Boolean, Single, Double, Decimal, DateTime, DateTimeOffset, TimeSpan, Guid, Enum, Collection, Complex, Dictionary, NullableInt32, NullableInt64, NullableInt16, NullableByte, NullableUInt16, NullableUInt32, NullableUInt64, NullableBoolean, NullableSingle, NullableDouble, NullableDecimal, NullableDateTime, NullableDateTimeOffset, NullableTimeSpan, NullableGuid, NullableEnum } /// /// Single source of truth for the compile-time decision: does the SGen-emit need a full ref-aware /// switch (Object / ObjectRefFirst / Null / ObjectRef / FixObj) for a /// given Complex property or collection element, OR can it use the zero-branch path /// (Object / Null / FixObj only)? /// /// Predicate semantics: the decision depends EXCLUSIVELY on whether the child /// element subtree may emit ref markers — captured by PropInfo.ChildNeedsRefScan / /// PropInfo.ElementNeedsRefScan. The parent-level EnableRefHandlingFeature flag is /// NOT a factor here — that flag governs only the parent's SELF-tracking emit in the scan /// pass (GenWriter.cs line 140), it does NOT suppress marker dispatch for child element /// properties of THIS type. /// /// Writer / reader symmetry — invoked from BOTH sides so the compile-time decision is /// identical at every call site: /// /// GenReader.EmitReadComplex — guards zero-branch vs full ref-aware switch. /// GenReader.EmitReadCollectionElement — same guard for collection-element dispatch. /// GenReader.EmitReadDictionary — same guard for dictionary-value dispatch. /// GenWriter.EmitDirectCollectionWrite — guards Object-only vs /// WriteObjectRefMarker* (runtime decide) emit on the writer side. /// /// /// Why a generator-only helper, not a runtime helper — the result is inlined into /// the generated code as either the zero-branch ag or the full-switch ag. The predicate runs /// once per emit-site at generation time; the runtime code has zero overhead from this abstraction /// (no method call, no branch on the runtime hot path). /// /// Regression target: AcBinarySerializerIIdReferenceTests.Serialize_RefMarkerCollectionElement_ParentRefHandlingFeatureOff_DriftReproduction. /// internal static class RefAwareEmitPredicate { /// /// Reader-side decision for a Complex property (EmitReadComplex) — does the /// emit need a full ref-aware switch on p.ChildNeedsRefScan? /// internal static bool ChildEmitsRefMarker(PropInfo p) => p.ChildNeedsRefScan; /// /// Reader-side decision for a collection element (EmitReadCollectionElement) and /// writer-side decision for the same element (EmitDirectCollectionWrite) — keyed on /// p.ElementNeedsRefScan. /// internal static bool ElementEmitsRefMarker(PropInfo p) => p.ElementNeedsRefScan; /// /// Reader-side overload for EmitReadCollectionElement when only the bool flag is in /// scope (e.g. when PropInfo is unrolled at the call site). Same semantics — kept as /// a thin overload so EVERY call site routes through this predicate, not the raw field. /// internal static bool ElementEmitsRefMarker(bool elementNeedsRefScan) => elementNeedsRefScan; /// /// Reader-side decision for a dictionary value (EmitReadDictionary) — keyed on /// p.DictValueNeedsRefScan. Symmetric with the Complex / Collection-element overloads. /// internal static bool DictValueEmitsRefMarker(PropInfo p) => p.DictValueNeedsRefScan; }