Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit dfa69b9

Browse files
committed
Add JsConfig.MaxDepth feature to prevent cyclical dependencies. fixes #331
1 parent ef02100 commit dfa69b9

File tree

8 files changed

+100
-16
lines changed

8 files changed

+100
-16
lines changed

src/ServiceStack.Text/Common/JsState.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23

34
namespace ServiceStack.Text.Common
45
{
@@ -16,5 +17,8 @@ internal static class JsState
1617

1718
[ThreadStatic]
1819
internal static bool QueryStringMode = false;
20+
21+
[ThreadStatic]
22+
internal static int Depth = 0;
1923
}
2024
}

src/ServiceStack.Text/JsConfig.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static JsConfigScope With(
4747
bool? alwaysUseUtc = null,
4848
bool? escapeUnicode = null,
4949
bool? includePublicFields = null,
50+
int? maxDepth = null,
5051
EmptyCtorFactoryDelegate modelFactory = null)
5152
{
5253
return new JsConfigScope {
@@ -68,6 +69,7 @@ public static JsConfigScope With(
6869
AlwaysUseUtc = alwaysUseUtc ?? sAlwaysUseUtc,
6970
EscapeUnicode = escapeUnicode ?? sEscapeUnicode,
7071
IncludePublicFields = includePublicFields ?? sIncludePublicFields,
72+
MaxDepth = maxDepth ?? sMaxDepth,
7173
ModelFactory = modelFactory ?? ModelFactory,
7274
};
7375
}
@@ -445,6 +447,24 @@ public static bool IncludePublicFields
445447
}
446448
}
447449

450+
/// <summary>
451+
/// Sets the maximum depth to avoid circular dependencies
452+
/// </summary>
453+
private static int? sMaxDepth;
454+
public static int MaxDepth
455+
{
456+
get
457+
{
458+
return (JsConfigScope.Current != null ? JsConfigScope.Current.MaxDepth : null)
459+
?? sMaxDepth
460+
?? int.MaxValue;
461+
}
462+
set
463+
{
464+
if (!sMaxDepth.HasValue) sMaxDepth = value;
465+
}
466+
}
467+
448468
/// <summary>
449469
/// Set this to enable your own type construction provider.
450470
/// This is helpful for integration with IoC containers where you need to call the container constructor.

src/ServiceStack.Text/JsConfigScope.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public void Dispose()
7575
public bool? EscapeUnicode { get; set; }
7676
public bool? PreferInterfaces { get; set; }
7777
public bool? IncludePublicFields { get; set; }
78+
public int? MaxDepth { get; set; }
7879
public EmptyCtorFactoryDelegate ModelFactory { get; set; }
7980
}
8081
}

src/ServiceStack.Text/Json/JsonWriter.Generic.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,32 @@ static JsonWriter()
172172
: JsonWriter.Instance.GetWriteFn<T>();
173173
}
174174

175-
public static void WriteObject(TextWriter writer, object value)
176-
{
175+
public static void WriteObject(TextWriter writer, object value)
176+
{
177177
#if MONOTOUCH
178178
if (writer == null) return;
179179
#endif
180-
CacheFn(writer, value);
181-
}
182-
}
180+
try
181+
{
182+
if (++JsState.Depth > JsConfig.MaxDepth)
183+
return;
184+
185+
CacheFn(writer, value);
186+
}
187+
finally
188+
{
189+
JsState.Depth--;
190+
}
191+
}
192+
193+
public static void WriteRootObject(TextWriter writer, object value)
194+
{
195+
#if MONOTOUCH
196+
if (writer == null) return;
197+
#endif
198+
JsState.Depth = 0;
199+
CacheFn(writer, value);
200+
}
201+
}
183202

184203
}

src/ServiceStack.Text/JsonSerializer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public static string SerializeToString<T>(T value)
7272
}
7373
else
7474
{
75-
JsonWriter<T>.WriteObject(writer, value);
75+
JsonWriter<T>.WriteRootObject(writer, value);
7676
}
7777
}
7878
return sb.ToString();
@@ -113,7 +113,7 @@ public static void SerializeToWriter<T>(T value, TextWriter writer)
113113
return;
114114
}
115115

116-
JsonWriter<T>.WriteObject(writer, value);
116+
JsonWriter<T>.WriteRootObject(writer, value);
117117
}
118118

119119
public static void SerializeToWriter(object value, Type type, TextWriter writer)
@@ -140,7 +140,7 @@ public static void SerializeToStream<T>(T value, Stream stream)
140140
}
141141

142142
var writer = new StreamWriter(stream, UTF8EncodingWithoutBom);
143-
JsonWriter<T>.WriteObject(writer, value);
143+
JsonWriter<T>.WriteRootObject(writer, value);
144144
writer.Flush();
145145
}
146146

src/ServiceStack.Text/Jsv/JsvWriter.Generic.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,32 @@ static JsvWriter()
120120
: JsvWriter.Instance.GetWriteFn<T>();
121121
}
122122

123-
public static void WriteObject(TextWriter writer, object value)
124-
{
123+
public static void WriteObject(TextWriter writer, object value)
124+
{
125125
#if MONOTOUCH
126126
if (writer == null) return;
127127
#endif
128-
CacheFn(writer, value);
129-
}
128+
try
129+
{
130+
if (++JsState.Depth > JsConfig.MaxDepth)
131+
return;
130132

131-
}
133+
CacheFn(writer, value);
134+
}
135+
finally
136+
{
137+
JsState.Depth--;
138+
}
139+
}
140+
141+
public static void WriteRootObject(TextWriter writer, object value)
142+
{
143+
#if MONOTOUCH
144+
if (writer == null) return;
145+
#endif
146+
JsState.Depth = 0;
147+
CacheFn(writer, value);
148+
}
149+
150+
}
132151
}

src/ServiceStack.Text/TypeSerializer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public static string SerializeToString<T>(T value)
9191
var sb = new StringBuilder();
9292
using (var writer = new StringWriter(sb, CultureInfo.InvariantCulture))
9393
{
94-
JsvWriter<T>.WriteObject(writer, value);
94+
JsvWriter<T>.WriteRootObject(writer, value);
9595
}
9696
return sb.ToString();
9797
}
@@ -125,7 +125,7 @@ public static void SerializeToWriter<T>(T value, TextWriter writer)
125125
return;
126126
}
127127

128-
JsvWriter<T>.WriteObject(writer, value);
128+
JsvWriter<T>.WriteRootObject(writer, value);
129129
}
130130

131131
public static void SerializeToWriter(object value, Type type, TextWriter writer)
@@ -152,7 +152,7 @@ public static void SerializeToStream<T>(T value, Stream stream)
152152
}
153153

154154
var writer = new StreamWriter(stream, UTF8EncodingWithoutBom);
155-
JsvWriter<T>.WriteObject(writer, value);
155+
JsvWriter<T>.WriteRootObject(writer, value);
156156
writer.Flush();
157157
}
158158

tests/ServiceStack.Text.Tests/CyclicalDependencyTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,26 @@ public void Can_serialize_Error()
152152
Assert.That(from.Contact, Is.EqualTo(dto.Contact));
153153
Assert.That(from.Notes, Is.EqualTo(dto.Notes));
154154
}
155+
156+
class person
157+
{
158+
public string name { get; set; }
159+
public person teacher { get; set; }
160+
}
161+
162+
[Test]
163+
public void Can_limit_cyclical_dependencies()
164+
{
165+
using (JsConfig.With(maxDepth:4))
166+
{
167+
var p = new person();
168+
p.teacher = new person { name = "sam", teacher = p };
169+
p.name = "bob";
170+
p.ToJsv().Print();
171+
p.ToJson().Print();
172+
}
173+
}
174+
175+
155176
}
156177
}

0 commit comments

Comments
 (0)