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
|
// Markerless types: write raw value only, no type marker, no PropertySkip
|
||||||
// Matches runtime WritePropertyMarkerless — these have ExpectedTypeCode
|
// Matches runtime WritePropertyMarkerless — these have ExpectedTypeCode
|
||||||
// NEVER filtered (runtime doesn't filter markerless properties either)
|
// 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))
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,8 +375,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Emits direct object write — bypasses GetWrapper + WriteObject entirely.
|
/// Emits direct object write — bypasses GetWrapper + WriteObject entirely.
|
||||||
/// Writes marker bytes + calls child GeneratedWriter.WriteProperties inline.
|
/// Writes marker bytes + calls child GeneratedWriter.WriteProperties inline.
|
||||||
/// For IId types: inlines TryConsumeWritePlanEntry for ref tracking cursor alignment.
|
/// IId types: guard ReferenceHandling != None (tracked in OnlyId + All).
|
||||||
/// Falls back to WriteObjectGenerated when context.IsDirectObjectWrite is false (UseMetadata/PropertyFilter).
|
/// Non-IId types: guard ReferenceHandling == All (tracked only in All mode).
|
||||||
|
/// Falls back to WriteObjectGenerated when context.IsDirectObjectWrite is false (UseMetadata).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void EmitDirectObjectWrite(StringBuilder sb, PropInfo p, string a, string i)
|
private static void EmitDirectObjectWrite(StringBuilder sb, PropInfo p, string a, string i)
|
||||||
{
|
{
|
||||||
|
|
@ -391,22 +401,22 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
||||||
sb.AppendLine($"{i} else");
|
sb.AppendLine($"{i} else");
|
||||||
sb.AppendLine($"{i} {{");
|
sb.AppendLine($"{i} {{");
|
||||||
|
|
||||||
if (p.IsIId)
|
// Inline ref tracking: guard depends on IId vs non-IId to match scan pass behavior.
|
||||||
{
|
// IId types: tracked in OnlyId + All → guard: ReferenceHandling != None
|
||||||
// IId type: inline ref tracking from WriteObject
|
// Non-IId types: tracked only in All → guard: ReferenceHandling == All
|
||||||
// context.UseTypeReferenceHandling gate: ReferenceHandling != None && IsIId
|
// This matches UseTypeReferenceHandling: (IsIId || ReferenceHandling == All) && ReferenceHandling != None
|
||||||
// For IId types, IsIId is always true, so: ReferenceHandling != None
|
var refGuard = p.IsIId
|
||||||
sb.AppendLine($"{i} if (context.ReferenceHandling != ReferenceHandlingMode.None && context.TryConsumeWritePlanEntry(out var pe_{p.Name}))");
|
? "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} {{");
|
||||||
sb.AppendLine($"{i} if (!pe_{p.Name}.IsFirst)");
|
sb.AppendLine($"{i} if (!pe_{p.Name}.IsFirst)");
|
||||||
sb.AppendLine($"{i} {{");
|
sb.AppendLine($"{i} {{");
|
||||||
// 2+ occurrence → ObjectRef + cacheIndex (no properties written)
|
|
||||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRef);");
|
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRef);");
|
||||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
||||||
sb.AppendLine($"{i} }}");
|
sb.AppendLine($"{i} }}");
|
||||||
sb.AppendLine($"{i} else");
|
sb.AppendLine($"{i} else");
|
||||||
sb.AppendLine($"{i} {{");
|
sb.AppendLine($"{i} {{");
|
||||||
// First occurrence → ObjectRefFirst + cacheIndex + properties
|
|
||||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRefFirst);");
|
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.ObjectRefFirst);");
|
||||||
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
sb.AppendLine($"{i} context.WriteVarUInt((uint)pe_{p.Name}.CacheMapIndex);");
|
||||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||||
|
|
@ -414,27 +424,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
||||||
sb.AppendLine($"{i} }}");
|
sb.AppendLine($"{i} }}");
|
||||||
sb.AppendLine($"{i} else");
|
sb.AppendLine($"{i} else");
|
||||||
sb.AppendLine($"{i} {{");
|
sb.AppendLine($"{i} {{");
|
||||||
// No ref tracking entry at this visit → plain Object marker
|
|
||||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({a}, context, {nextDepth});");
|
||||||
sb.AppendLine($"{i} }}");
|
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} }}");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendLine($"{i} }}");
|
sb.AppendLine($"{i} }}");
|
||||||
sb.AppendLine($"{i}}}");
|
sb.AppendLine($"{i}}}");
|
||||||
|
|
@ -508,9 +500,13 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
||||||
sb.AppendLine($"{i} if ({e} == null) {{ context.WriteByte(BinaryTypeCode.Null); continue; }}");
|
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; }}");
|
sb.AppendLine($"{i} if (nextDepth_{p.Name} > context.MaxDepth) {{ context.WriteByte(BinaryTypeCode.Null); continue; }}");
|
||||||
|
|
||||||
if (p.ElementIsIId)
|
// 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
|
||||||
sb.AppendLine($"{i} if (context.ReferenceHandling != ReferenceHandlingMode.None && context.TryConsumeWritePlanEntry(out var epe_{p.Name}))");
|
// 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} {{");
|
||||||
sb.AppendLine($"{i} if (!epe_{p.Name}.IsFirst)");
|
sb.AppendLine($"{i} if (!epe_{p.Name}.IsFirst)");
|
||||||
sb.AppendLine($"{i} {{");
|
sb.AppendLine($"{i} {{");
|
||||||
|
|
@ -529,20 +525,6 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
||||||
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
sb.AppendLine($"{i} context.WriteByte(BinaryTypeCode.Object);");
|
||||||
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
sb.AppendLine($"{i} {writer}.Instance.WriteProperties({e}, context, nextDepth_{p.Name});");
|
||||||
sb.AppendLine($"{i} }}");
|
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} }}");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendLine($"{i} }}");
|
sb.AppendLine($"{i} }}");
|
||||||
sb.AppendLine($"{i}}}");
|
sb.AppendLine($"{i}}}");
|
||||||
|
|
|
||||||
|
|
@ -1089,7 +1089,7 @@ public static partial class AcBinarySerializer
|
||||||
if (context.UseGeneratedCode)
|
if (context.UseGeneratedCode)
|
||||||
{
|
{
|
||||||
var generatedWriter = wrapper.GeneratedWriter;
|
var generatedWriter = wrapper.GeneratedWriter;
|
||||||
if (generatedWriter != null && !context.UseMetadata)
|
if (generatedWriter != null)
|
||||||
{
|
{
|
||||||
generatedWriter.WriteProperties(value, context, nextDepth);
|
generatedWriter.WriteProperties(value, context, nextDepth);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
||||||
/// allowing the deserializer to match properties by name between different types.
|
/// allowing the deserializer to match properties by name between different types.
|
||||||
/// Default: false (no overhead)
|
/// Default: false (no overhead)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseMetadata { get; set; } = false;
|
public bool UseMetadata { get; set; } = true;
|
||||||
|
|
||||||
public bool UseGeneratedCode { get; set; } = true;
|
public bool UseGeneratedCode { get; set; } = true;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue