Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Suggested fix for 'Possible Recursion Detected' exception on specific scenario. #836

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/Examples/Recursion.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.IO;
using Xunit;
using ProtoBuf;
using System.Collections.Generic;
using System.Linq;

namespace Examples
{
Expand All @@ -10,6 +12,45 @@ public class RecursiveObject
[ProtoMember(1)]
public RecursiveObject Yeuch { get; set; }
}

[ProtoContract]
public class MySurrogate
{
[ProtoMember(1)]
public TreeNode treeNode { get; set; }
[ProtoConverter]
public static INode From(MySurrogate surrogate)
{
return surrogate.treeNode;
}
[ProtoConverter]
public static MySurrogate To(INode value)
{
var surrogate = new MySurrogate();
if (value is TreeNode treeNode)
surrogate.treeNode = treeNode;
return surrogate;
}
}
[ProtoContract(Surrogate = typeof(MySurrogate))]
public interface INode
{
public string Name { get; }
}
[ProtoContract]
public class TreeNode : INode
{
[ProtoMember(1)]
public string Name { get; }
[ProtoMember(2)]
public List<INode> Children { get; }
public TreeNode(string Name) : this()
{
this.Name = Name;
}
public TreeNode() { Children = new List<INode>(); }
public string AsString() => Children != null ? $"{Name} => {{{string.Join(",", Children)}}}" : Name;
}

public class Recursion
{
Expand All @@ -23,5 +64,26 @@ public void BlowUp()
Serializer.Serialize(Stream.Null, obj);
});
}

[Fact]
public void Tree_NoRecursionDetected()
{
var tree = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToArray().Reverse().Select(letter => new TreeNode(letter.ToString()))
.Aggregate((prev, next) => { next.Children.Add(prev); return next; });
//A => {B => {C => {D => {E => {F => {G => {H => {I => {J => {K => {L => {M => {N => {...}}}}}}}}}}}}}}
var cloned = Serializer.DeepClone(tree);
Assert.Equal(tree.AsString(), cloned.AsString());
}

[Fact]
public void Graph_RecursionDetected()
{
Program.ExpectFailure<ProtoException>(() =>
{
var graph = new TreeNode("A");
graph.Children.Add(graph);
Serializer.DeepClone(graph);
});
}
}
}
2 changes: 1 addition & 1 deletion src/protobuf-net.Core/ProtoWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ protected private virtual void ClearKnownObjects()
protected internal virtual void WriteMessage<[DynamicallyAccessedMembers(DynamicAccess.ContractType)] T>(ref State state, T value, ISerializer<T> serializer, PrefixStyle style, bool recursionCheck)
{
#pragma warning disable CS0618 // StartSubItem/EndSubItem
var tok = state.StartSubItem(TypeHelper<T>.IsReferenceType & recursionCheck ? (object)value : null, style);
var tok = state.StartSubItem(TypeHelper<T>.IsReferenceType & !typeof(T).IsInterface & recursionCheck ? (object)value : null, style);
(serializer ?? TypeModel.GetSerializer<T>(model)).Write(ref state, value);
state.EndSubItem(tok, style);
#pragma warning restore CS0618
Expand Down