Skip to content

Commit 9a05a92

Browse files
committed
add (Type|Member)DeclarationOptions.NullabilityInfoContextLockObject to support locking NullabilityInfoContext
1 parent de01132 commit 9a05a92

File tree

4 files changed

+391
-1
lines changed

4 files changed

+391
-1
lines changed

src/Smdn.Reflection.ReverseGenerating/Smdn.Reflection.ReverseGenerating/Generator.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ GeneratorOptions options
386386
#pragma warning disable SA1114
387387
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
388388
nullabilityInfoContext: memberOptions.NullabilityInfoContext,
389+
nullabilityInfoContextLockObject: memberOptions.NullabilityInfoContextLockObject,
389390
#endif
390391
typeWithNamespace: memberOptions.WithNamespace
391392
#pragma warning restore SA1114
@@ -509,6 +510,7 @@ GeneratorOptions options
509510
#pragma warning disable SA1114
510511
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
511512
nullabilityInfoContext: memberOptions.NullabilityInfoContext,
513+
nullabilityInfoContextLockObject: memberOptions.NullabilityInfoContextLockObject,
512514
#endif
513515
typeWithNamespace: memberOptions.WithNamespace
514516
#pragma warning restore SA1114
@@ -744,6 +746,9 @@ GeneratorOptions options
744746
NullabilityInfoContext = asDelegateDeclaration
745747
? options.TypeDeclaration.NullabilityInfoContext
746748
: options.MemberDeclaration.NullabilityInfoContext,
749+
NullabilityInfoContextLockObject = asDelegateDeclaration
750+
? options.TypeDeclaration.NullabilityInfoContextLockObject
751+
: options.MemberDeclaration.NullabilityInfoContextLockObject,
747752
#endif
748753
FormatTypeWithNamespace = asDelegateDeclaration
749754
? options.TypeDeclaration.WithNamespace
@@ -765,6 +770,7 @@ GeneratorOptions options
765770
#pragma warning disable SA1114
766771
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
767772
nullabilityInfoContext: formattingOptions.NullabilityInfoContext,
773+
nullabilityInfoContextLockObject: formattingOptions.NullabilityInfoContextLockObject,
768774
#endif
769775
typeWithNamespace: formattingOptions.FormatTypeWithNamespace
770776
#pragma warning restore SA1114
@@ -991,7 +997,9 @@ GeneratorOptions options
991997
nullabilityInfoContext: isDelegate
992998
? options.TypeDeclaration.NullabilityInfoContext
993999
: options.MemberDeclaration.NullabilityInfoContext,
994-
nullabilityInfoContextLockObject: null, // TODO
1000+
nullabilityInfoContextLockObject: isDelegate
1001+
? options.TypeDeclaration.NullabilityInfoContextLockObject
1002+
: options.MemberDeclaration.NullabilityInfoContextLockObject,
9951003
#endif
9961004
typeWithNamespace: options.ParameterDeclaration.WithNamespace,
9971005
typeWithDeclaringTypeName: options.ParameterDeclaration.WithDeclaringTypeName,
@@ -1073,6 +1081,7 @@ GeneratorOptions options
10731081
#pragma warning disable SA1114
10741082
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
10751083
nullabilityInfoContext: memberOptions.NullabilityInfoContext,
1084+
nullabilityInfoContextLockObject: memberOptions.NullabilityInfoContextLockObject,
10761085
#endif
10771086
typeWithNamespace: memberOptions.WithNamespace
10781087
#pragma warning restore SA1114

src/Smdn.Reflection.ReverseGenerating/Smdn.Reflection.ReverseGenerating/GeneratorOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class TypeDeclarationOptions {
3737
public bool OmitEnumUnderlyingTypeIfPossible { get; set; } = false;
3838
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
3939
public NullabilityInfoContext? NullabilityInfoContext { get; set; } = new();
40+
public object? NullabilityInfoContextLockObject { get; set; }
4041
#endif
4142

4243
internal TypeDeclarationOptions Clone()
@@ -55,6 +56,7 @@ public class MemberDeclarationOptions {
5556
public MethodBodyOption AccessorBody { get; set; } = MethodBodyOption.EmptyImplementation;
5657
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
5758
public NullabilityInfoContext? NullabilityInfoContext { get; set; } = new();
59+
public object? NullabilityInfoContextLockObject { get; set; }
5860
#endif
5961

6062
internal MemberDeclarationOptions Clone()

tests/Smdn.Reflection.ReverseGenerating/Smdn.Reflection.ReverseGenerating/Generator.GenerateMemberDeclaration.cs

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Reflection;
99
using System.Runtime.CompilerServices;
10+
using System.Threading.Tasks;
1011
using NUnit.Framework;
1112

1213
namespace Smdn.Reflection.ReverseGenerating;
@@ -92,4 +93,259 @@ MemberInfo member
9293
message: $"{attrTestCase.SourceLocation} ({member.DeclaringType?.FullName}.{member.Name})"
9394
);
9495
}
96+
97+
#if SYSTEM_REFLECTION_NULLABILITYINFOCONTEXT
98+
#nullable enable annotations
99+
#pragma warning disable CS0649
100+
private class TestClassForGenerateMemberDeclarationConcurrently {
101+
public Tuple<string?>? F1;
102+
public Tuple<string?, string?>? F2;
103+
public Tuple<string?, string?, string?>? F3;
104+
public Tuple<string?, string?, string?, string?>? F4;
105+
public Tuple<string?, string?, string?, string?, string?>? F5;
106+
public Tuple<string?, string?, string?, string?, string?, string?>? F6;
107+
public Tuple<string?, string?, string?, string?, string?, string?, string?>? F7;
108+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?>>? F8;
109+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?>>? F9;
110+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?>>? F10;
111+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?>>? F11;
112+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?, string?>>? F12;
113+
114+
public Tuple<string?>? P1 { get; set; }
115+
public Tuple<string?, string?>? P2 { get; set; }
116+
public Tuple<string?, string?, string?>? P3 { get; set; }
117+
public Tuple<string?, string?, string?, string?>? P4 { get; set; }
118+
public Tuple<string?, string?, string?, string?, string?>? P5 { get; set; }
119+
public Tuple<string?, string?, string?, string?, string?, string?>? P6 { get; set; }
120+
public Tuple<string?, string?, string?, string?, string?, string?, string?>? P7 { get; set; }
121+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?>>? P8 { get; set; }
122+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?>>? P9 { get; set; }
123+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?>>? P10 { get; set; }
124+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?>>? P11 { get; set; }
125+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?, string?>>? P12 { get; set; }
126+
127+
public Tuple<string?>? MRP1() => throw new NotImplementedException();
128+
public Tuple<string?, string?>? MRP2() => throw new NotImplementedException();
129+
public Tuple<string?, string?, string?>? MRP3() => throw new NotImplementedException();
130+
public Tuple<string?, string?, string?, string?>? MRP4() => throw new NotImplementedException();
131+
public Tuple<string?, string?, string?, string?, string?>? MRP5() => throw new NotImplementedException();
132+
public Tuple<string?, string?, string?, string?, string?, string?>? MRP6() => throw new NotImplementedException();
133+
public Tuple<string?, string?, string?, string?, string?, string?, string?>? MRP7() => throw new NotImplementedException();
134+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?>>? MRP8() => throw new NotImplementedException();
135+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?>>? MRP9() => throw new NotImplementedException();
136+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?>>? MRP10() => throw new NotImplementedException();
137+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?>>? MRP11() => throw new NotImplementedException();
138+
public Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?, string?>>? MRP12() => throw new NotImplementedException();
139+
140+
public void MP1(Tuple<string?>? p) => throw new NotImplementedException();
141+
public void MP2(Tuple<string?, string?>? p) => throw new NotImplementedException();
142+
public void MP3(Tuple<string?, string?, string?>? p) => throw new NotImplementedException();
143+
public void MP4(Tuple<string?, string?, string?, string?>? p) => throw new NotImplementedException();
144+
public void MP5(Tuple<string?, string?, string?, string?, string?>? p) => throw new NotImplementedException();
145+
public void MP6(Tuple<string?, string?, string?, string?, string?, string?>? p) => throw new NotImplementedException();
146+
public void MP7(Tuple<string?, string?, string?, string?, string?, string?, string?>? p) => throw new NotImplementedException();
147+
public void MP8(Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?>>? p) => throw new NotImplementedException();
148+
public void MP9(Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?>>? p) => throw new NotImplementedException();
149+
public void MP10(Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?>>? p) => throw new NotImplementedException();
150+
public void MP11(Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?>>? p) => throw new NotImplementedException();
151+
public void MP12(Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?, string?>>? p) => throw new NotImplementedException();
152+
153+
public event EventHandler<Tuple<string?>?>? E1;
154+
public event EventHandler<Tuple<string?, string?>?>? E2;
155+
public event EventHandler<Tuple<string?, string?, string?>?>? E3;
156+
public event EventHandler<Tuple<string?, string?, string?, string?>?>? E4;
157+
public event EventHandler<Tuple<string?, string?, string?, string?, string?>?>? E5;
158+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?>?>? E6;
159+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?, string?>?>? E7;
160+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?>>?>? E8;
161+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?>>?>? E9;
162+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?>>?>? E10;
163+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?>>?>? E11;
164+
public event EventHandler<Tuple<string?, string?, string?, string?, string?, string?, string?, Tuple<string?, string?, string?, string?, string?>>?>? E12;
165+
}
166+
#pragma warning restore CS0649
167+
168+
private async Task GenerateMemberDeclaration_Concurrent_Async(
169+
bool lockCreatingNullabilityInfo,
170+
Action<GeneratorOptions> testAction
171+
)
172+
{
173+
const int maxNumberOfRepeat = 30;
174+
175+
Action<NullabilityInfoContext, object?> wrappedTestAction = new(
176+
(ctx, lockObject) => {
177+
var options = new GeneratorOptions();
178+
179+
options.MemberDeclaration.NullabilityInfoContext = ctx;
180+
options.MemberDeclaration.NullabilityInfoContextLockObject = lockObject;
181+
182+
testAction(options);
183+
}
184+
);
185+
186+
if (lockCreatingNullabilityInfo) {
187+
await CSharpFormatterTests.AssertNullabilityInfoContextConcurrentOperationWithLockMustNotThrowExceptionAsync(
188+
wrappedTestAction,
189+
maxNumberOfRepeat
190+
).ConfigureAwait(false);
191+
}
192+
else {
193+
await CSharpFormatterTests.AssertNullabilityInfoContextConcurrentOperationWithoutLockMustThrowExceptionAsync(
194+
wrappedTestAction,
195+
maxNumberOfRepeat
196+
).ConfigureAwait(false);
197+
}
198+
}
199+
200+
[TestCase(true)]
201+
[TestCase(false)]
202+
public Task GenerateMemberDeclaration_Concurrent_FieldInfo(bool lockCreatingNullabilityInfo)
203+
{
204+
var t = typeof(TestClassForGenerateMemberDeclarationConcurrently);
205+
var fields = new[] {
206+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F1))!,
207+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F2))!,
208+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F3))!,
209+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F4))!,
210+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F5))!,
211+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F6))!,
212+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F7))!,
213+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F8))!,
214+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F9))!,
215+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F10))!,
216+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F11))!,
217+
t.GetField(nameof(TestClassForGenerateMemberDeclarationConcurrently.F12))!,
218+
};
219+
220+
return GenerateMemberDeclaration_Concurrent_Async(
221+
lockCreatingNullabilityInfo,
222+
options => {
223+
foreach (var field in fields) {
224+
Generator.GenerateMemberDeclaration(field, null, options);
225+
}
226+
}
227+
);
228+
}
229+
230+
[TestCase(true)]
231+
[TestCase(false)]
232+
public Task GenerateMemberDeclaration_Concurrent_PropertyInfo(bool lockCreatingNullabilityInfo)
233+
{
234+
var t = typeof(TestClassForGenerateMemberDeclarationConcurrently);
235+
var properties = new[] {
236+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P1))!,
237+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P2))!,
238+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P3))!,
239+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P4))!,
240+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P5))!,
241+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P6))!,
242+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P7))!,
243+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P8))!,
244+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P9))!,
245+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P10))!,
246+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P11))!,
247+
t.GetProperty(nameof(TestClassForGenerateMemberDeclarationConcurrently.P12))!,
248+
};
249+
250+
return GenerateMemberDeclaration_Concurrent_Async(
251+
lockCreatingNullabilityInfo,
252+
options => {
253+
foreach (var property in properties) {
254+
Generator.GenerateMemberDeclaration(property, null, options);
255+
}
256+
}
257+
);
258+
}
259+
260+
[TestCase(true)]
261+
[TestCase(false)]
262+
public Task GenerateMemberDeclaration_Concurrent_MethodInfo_Parameter(bool lockCreatingNullabilityInfo)
263+
{
264+
var t = typeof(TestClassForGenerateMemberDeclarationConcurrently);
265+
var methods = new[] {
266+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP1))!,
267+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP2))!,
268+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP3))!,
269+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP4))!,
270+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP5))!,
271+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP6))!,
272+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP7))!,
273+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP8))!,
274+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP9))!,
275+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP10))!,
276+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP11))!,
277+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MP12))!,
278+
};
279+
280+
return GenerateMemberDeclaration_Concurrent_Async(
281+
lockCreatingNullabilityInfo,
282+
options => {
283+
foreach (var method in methods) {
284+
Generator.GenerateMemberDeclaration(method, null, options);
285+
}
286+
}
287+
);
288+
}
289+
290+
[TestCase(true)]
291+
[TestCase(false)]
292+
public Task GenerateMemberDeclaration_Concurrent_MethodInfo_ReturnParameter(bool lockCreatingNullabilityInfo)
293+
{
294+
var t = typeof(TestClassForGenerateMemberDeclarationConcurrently);
295+
var methods = new[] {
296+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP1))!,
297+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP2))!,
298+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP3))!,
299+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP4))!,
300+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP5))!,
301+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP6))!,
302+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP7))!,
303+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP8))!,
304+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP9))!,
305+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP10))!,
306+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP11))!,
307+
t.GetMethod(nameof(TestClassForGenerateMemberDeclarationConcurrently.MRP12))!,
308+
};
309+
310+
return GenerateMemberDeclaration_Concurrent_Async(
311+
lockCreatingNullabilityInfo,
312+
options => {
313+
foreach (var method in methods) {
314+
Generator.GenerateMemberDeclaration(method, null, options);
315+
}
316+
}
317+
);
318+
}
319+
320+
[TestCase(true)]
321+
[TestCase(false)]
322+
public Task GenerateMemberDeclaration_Concurrent_EventInfo(bool lockCreatingNullabilityInfo)
323+
{
324+
var t = typeof(TestClassForGenerateMemberDeclarationConcurrently);
325+
var events = new[] {
326+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E1))!,
327+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E2))!,
328+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E3))!,
329+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E4))!,
330+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E5))!,
331+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E6))!,
332+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E7))!,
333+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E8))!,
334+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E9))!,
335+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E10))!,
336+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E11))!,
337+
t.GetEvent(nameof(TestClassForGenerateMemberDeclarationConcurrently.E12))!,
338+
};
339+
340+
return GenerateMemberDeclaration_Concurrent_Async(
341+
lockCreatingNullabilityInfo,
342+
options => {
343+
foreach (var @event in events) {
344+
Generator.GenerateMemberDeclaration(@event, null, options);
345+
}
346+
}
347+
);
348+
}
349+
#nullable restore annotations
350+
#endif
95351
}

0 commit comments

Comments
 (0)