Refine AcBinary gen: metadata, ref tracking alignment
Improve generated serialization code to match runtime behavior for metadata emission and reference tracking. Markerless types now respect UseMetadata, ensuring type markers are written when required. Ref tracking guards for IId and non-IId types are unified to match scan pass logic. Generated writers are always used when available. Default UseMetadata is now true for consistent metadata output.
This commit is contained in:
parent
d40e40a45a
commit
e2269d3ecf
|
|
@ -248,9 +248,18 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
// Markerless types: write raw value only, no type marker, no PropertySkip
|
||||
// Matches runtime WritePropertyMarkerless — these have ExpectedTypeCode
|
||||
// NEVER filtered (runtime doesn't filter markerless properties either)
|
||||
// UseMetadata=true: markerless path NOT available — must use markered path (EmitSkip)
|
||||
// to match runtime WritePropertyOrSkip behavior (every property gets a type marker byte)
|
||||
if (IsMarkerless(p.TypeKind))
|
||||
{
|
||||
EmitMarkerless(sb, p.TypeKind, a, i);
|
||||
sb.AppendLine($"{i}if (context.UseMetadata)");
|
||||
sb.AppendLine($"{i}{{");
|
||||
EmitSkip(sb, p.TypeKind, a, p.TypeNameForTypeof, i + " ");
|
||||
sb.AppendLine($"{i}}}");
|
||||
sb.AppendLine($"{i}else");
|
||||
sb.AppendLine($"{i}{{");
|
||||
EmitMarkerless(sb, p.TypeKind, a, i + " ");
|
||||
sb.AppendLine($"{i}}}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -366,8 +375,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
/// <summary>
|
||||
/// Emits direct object write — bypasses GetWrapper + WriteObject entirely.
|
||||
/// Writes marker bytes + calls child GeneratedWriter.WriteProperties inline.
|
||||
/// For IId types: inlines TryConsumeWritePlanEntry for ref tracking cursor alignment.
|
||||
/// Falls back to WriteObjectGenerated when context.IsDirectObjectWrite is false (UseMetadata/PropertyFilter).
|
||||
/// IId types: guard ReferenceHandling != None (tracked in OnlyId + All).
|
||||
/// Non-IId types: guard ReferenceHandling == All (tracked only in All mode).
|
||||
/// Falls back to WriteObjectGenerated when context.IsDirectObjectWrite is false (UseMetadata).
|
||||
/// </summary>
|
||||
private static void EmitDirectObjectWrite(StringBuilder sb, PropInfo p, string a, string i)
|
||||
{
|
||||
|
|
@ -391,50 +401,32 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
|
||||
if (p.IsIId)
|
||||
{
|
||||
// IId type: inline ref tracking from WriteObject
|
||||
// context.UseTypeReferenceHandling gate: ReferenceHandling != None && IsIId
|
||||
// For IId types, IsIId is always true, so: ReferenceHandling != None
|
||||
sb.AppendLine($"{i} if (context.ReferenceHandling != ReferenceHandlingMode.None && context.TryConsumeWritePlanEntry(out var pe_{p.Name}))");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} if (!pe_{p.Name}.IsFirst)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
// 2+ occurrence → ObjectRef + cacheIndex (no properties written)
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRef);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
// First occurrence → ObjectRefFirst + cacheIndex + properties
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRefFirst);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
// No ref tracking entry at this visit → plain Object marker
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-IId type: usually no ref tracking needed.
|
||||
// Exception: ReferenceHandling == All tracks ALL objects → scan pass increments ScanVisitIndex,
|
||||
// so write pass must also consume from WritePlan to keep cursor aligned.
|
||||
// Guard: fall back to WriteObjectGenerated for All mode (rare). OnlyId mode is safe (non-IId skipped).
|
||||
sb.AppendLine($"{i} if (context.ReferenceHandling == ReferenceHandlingMode.All)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.WriteObjectGenerated({a}, typeof({p.TypeNameForTypeof}), context, depth);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
// Inline ref tracking: guard depends on IId vs non-IId to match scan pass behavior.
|
||||
// IId types: tracked in OnlyId + All → guard: ReferenceHandling != None
|
||||
// Non-IId types: tracked only in All → guard: ReferenceHandling == All
|
||||
// This matches UseTypeReferenceHandling: (IsIId || ReferenceHandling == All) && ReferenceHandling != None
|
||||
var refGuard = p.IsIId
|
||||
? "context.ReferenceHandling != ReferenceHandlingMode.None"
|
||||
: "context.ReferenceHandling == ReferenceHandlingMode.All";
|
||||
sb.AppendLine($"{i} if ({refGuard} && context.TryConsumeWritePlanEntry(out var pe_{p.Name}))");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} if (!pe_{p.Name}.IsFirst)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRef);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRefFirst);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i}}}");
|
||||
|
|
@ -508,41 +500,31 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} if ({e} == null) {{ context.WriteByte(BinaryTypeCode.Null); continue; }}");
|
||||
sb.AppendLine($"{i} if (nextDepth_{p.Name} > context.MaxDepth) {{ context.WriteByte(BinaryTypeCode.Null); continue; }}");
|
||||
|
||||
if (p.ElementIsIId)
|
||||
{
|
||||
sb.AppendLine($"{i} if (context.ReferenceHandling != ReferenceHandlingMode.None && context.TryConsumeWritePlanEntry(out var epe_{p.Name}))");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} if (!epe_{p.Name}.IsFirst)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRef);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)epe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRefFirst);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)epe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-IId element: guard against ReferenceHandling.All (cursor alignment)
|
||||
sb.AppendLine($"{i} if (context.ReferenceHandling == ReferenceHandlingMode.All)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} AcBinarySerializer.WriteObjectGenerated({e}, typeof({elemType}), context, nextDepth_{p.Name} - 1);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
}
|
||||
// Inline ref tracking: guard depends on IId vs non-IId element type to match scan pass.
|
||||
// IId elements: tracked in OnlyId + All → guard: ReferenceHandling != None
|
||||
// Non-IId elements: tracked only in All → guard: ReferenceHandling == All
|
||||
var elemRefGuard = p.ElementIsIId
|
||||
? "context.ReferenceHandling != ReferenceHandlingMode.None"
|
||||
: "context.ReferenceHandling == ReferenceHandlingMode.All";
|
||||
sb.AppendLine($"{i} if ({elemRefGuard} && context.TryConsumeWritePlanEntry(out var epe_{p.Name}))");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} if (!epe_{p.Name}.IsFirst)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRef);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)epe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRefFirst);");
|
||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)epe_{p.Name}.CacheMapIndex);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} else");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i}}}");
|
||||
|
|
|
|||
|
|
@ -1089,7 +1089,7 @@ public static partial class AcBinarySerializer
|
|||
if (context.UseGeneratedCode)
|
||||
{
|
||||
var generatedWriter = wrapper.GeneratedWriter;
|
||||
if (generatedWriter != null && !context.UseMetadata)
|
||||
if (generatedWriter != null)
|
||||
{
|
||||
generatedWriter.WriteProperties(value, context, nextDepth);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
|||
/// allowing the deserializer to match properties by name between different types.
|
||||
/// Default: false (no overhead)
|
||||
/// </summary>
|
||||
public bool UseMetadata { get; set; } = false;
|
||||
public bool UseMetadata { get; set; } = true;
|
||||
|
||||
public bool UseGeneratedCode { get; set; } = true;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue