Skip to content

Commit f6e7568

Browse files
Add test coverage for custom modifiers (dotnet#20546)
Custom modifiers are only lightly tested within the CLR test codebase (both closed and open). This adds targeted tests for: * Resolution and overriding * Various places that should ignore them * Reflection
1 parent a940030 commit f6e7568

File tree

6 files changed

+485
-0
lines changed

6 files changed

+485
-0
lines changed
+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Reflection;
7+
8+
class Program
9+
{
10+
static int Main()
11+
{
12+
var baseClass = new BaseClass();
13+
var derivedClass = new DerivedClass();
14+
var superDerivedClass = new SuperDerivedClass();
15+
16+
//
17+
// C# will generate a call to the unmodified version of the method so this is easy to check statically
18+
//
19+
20+
if (baseClass.Override(0) != 3)
21+
return 1;
22+
23+
if (derivedClass.Override(0) != 103)
24+
return 2;
25+
26+
if (superDerivedClass.Override(0) != 203)
27+
return 3;
28+
29+
//
30+
// Reflection-locate rest of the `Override` method overloads
31+
//
32+
33+
MethodInfo paramModoptFoo = null;
34+
MethodInfo paramModoptBar = null;
35+
MethodInfo paramModreqFoo = null;
36+
MethodInfo paramUnmodified = null;
37+
MethodInfo returnModoptFoo = null;
38+
MethodInfo arrayModopt1 = null;
39+
MethodInfo arrayModopt2 = null;
40+
foreach (var method in typeof(BaseClass).GetTypeInfo().DeclaredMethods)
41+
{
42+
ParameterInfo param = method.GetParameters()[0];
43+
Type[] paramRequiredModifiers = param.GetRequiredCustomModifiers();
44+
Type[] paramOptionalModifiers = param.GetOptionalCustomModifiers();
45+
46+
ParameterInfo retParam = method.ReturnParameter;
47+
Type[] retParamOptionalModifiers = retParam.GetOptionalCustomModifiers();
48+
49+
if (param.ParameterType != typeof(int) && param.ParameterType != typeof(int[]))
50+
throw new Exception();
51+
52+
if (paramRequiredModifiers.Length > 0)
53+
{
54+
if (paramRequiredModifiers.Length > 1 || paramRequiredModifiers[0] != typeof(FooModifier))
55+
throw new Exception();
56+
else
57+
paramModreqFoo = method;
58+
}
59+
else if (paramOptionalModifiers.Length > 0)
60+
{
61+
if (paramOptionalModifiers.Length > 1)
62+
throw new Exception();
63+
else if (paramOptionalModifiers[0] == typeof(FooModifier))
64+
paramModoptFoo = method;
65+
else if (paramOptionalModifiers[0] == typeof(BarModifier))
66+
paramModoptBar = method;
67+
else
68+
throw new Exception();
69+
}
70+
else if (retParamOptionalModifiers.Length > 0)
71+
{
72+
if (retParamOptionalModifiers.Length > 1 || retParamOptionalModifiers[0] != typeof(FooModifier))
73+
throw new Exception();
74+
else
75+
returnModoptFoo = method;
76+
}
77+
else
78+
{
79+
if (param.ParameterType == typeof(int))
80+
paramUnmodified = method;
81+
else if (param.ParameterType == typeof(int[]))
82+
{
83+
// Reflection can't distinguish between the two overloads
84+
85+
if (arrayModopt1 == null)
86+
arrayModopt1 = method;
87+
else if (arrayModopt2 == null)
88+
arrayModopt2 = method;
89+
else
90+
throw new Exception();
91+
}
92+
else
93+
throw new Exception();
94+
}
95+
}
96+
97+
if ((int)paramModoptFoo.Invoke(baseClass, new object[] { 0 }) != 0)
98+
return 101;
99+
if ((int)paramModoptBar.Invoke(baseClass, new object[] { 0 }) != 1)
100+
return 102;
101+
if ((int)paramModreqFoo.Invoke(baseClass, new object[] { 0 }) != 2)
102+
return 103;
103+
if ((int)paramUnmodified.Invoke(baseClass, new object[] { 0 }) != 3)
104+
return 104;
105+
if ((int)returnModoptFoo.Invoke(baseClass, new object[] { 0 }) != 4)
106+
return 105;
107+
108+
if ((int)paramModoptFoo.Invoke(derivedClass, new object[] { 0 }) != 100)
109+
return 201;
110+
if ((int)paramModoptBar.Invoke(derivedClass, new object[] { 0 }) != 101)
111+
return 202;
112+
if ((int)paramModreqFoo.Invoke(derivedClass, new object[] { 0 }) != 102)
113+
return 203;
114+
if ((int)paramUnmodified.Invoke(derivedClass, new object[] { 0 }) != 103)
115+
return 204;
116+
if ((int)returnModoptFoo.Invoke(derivedClass, new object[] { 0 }) != 104)
117+
return 205;
118+
119+
if ((int)arrayModopt1.Invoke(baseClass, new object[] { null }) + 100 != (int)arrayModopt1.Invoke(derivedClass, new object[] { null }))
120+
return 301;
121+
122+
if ((int)arrayModopt2.Invoke(baseClass, new object[] { null }) + 100 != (int)arrayModopt2.Invoke(derivedClass, new object[] { null }))
123+
return 302;
124+
125+
//
126+
// Make sure modifiers are ignored for newobj/box
127+
//
128+
129+
object tryAllocWithModifiedArrayResult = Factory.TryAllocWithModifiedArray();
130+
if (!(tryAllocWithModifiedArrayResult is GenericClass<int[]>))
131+
return 401;
132+
if (tryAllocWithModifiedArrayResult.GetType() != typeof(GenericClass<int[]>))
133+
return 402;
134+
135+
object tryBoxWithModifiedPointerResult = Factory.TryBoxWithModifiedPointer();
136+
if (!(tryBoxWithModifiedPointerResult is GenericStruct<int*[]>))
137+
return 501;
138+
if (tryBoxWithModifiedPointerResult.GetType() != typeof(GenericStruct<int*[]>))
139+
return 502;
140+
141+
return 100;
142+
}
143+
144+
class SuperDerivedClass : DerivedClass
145+
{
146+
public override int Override(int A_0)
147+
{
148+
Console.WriteLine("In int32 SuperDerivedClass::Override(int32)");
149+
return 203;
150+
}
151+
}
152+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
4+
<PropertyGroup>
5+
<AssemblyName>modifiers</AssemblyName>
6+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
7+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
8+
<SchemaVersion>2.0</SchemaVersion>
9+
<ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
10+
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
11+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
12+
<OutputType>Exe</OutputType>
13+
<CLRTestKind>BuildAndRun</CLRTestKind>
14+
<CLRTestPriority>0</CLRTestPriority>
15+
</PropertyGroup>
16+
<ItemGroup>
17+
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
18+
<Visible>False</Visible>
19+
</CodeAnalysisDependentAssemblyPaths>
20+
</ItemGroup>
21+
<ItemGroup>
22+
<Compile Include="modifiers.cs" />
23+
</ItemGroup>
24+
<ItemGroup>
25+
<ProjectReference Include="modifiersdata.ilproj" />
26+
</ItemGroup>
27+
<ItemGroup>
28+
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
29+
</ItemGroup>
30+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
31+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
.assembly extern mscorlib { }
6+
.assembly modifiersdata { }
7+
8+
.class public BaseClass
9+
{
10+
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
11+
{
12+
ldarg.0
13+
call instance void [mscorlib]System.Object::.ctor()
14+
ret
15+
}
16+
17+
.method public hidebysig virtual newslot instance int32 Override(int32 modopt (FooModifier))
18+
{
19+
ldstr "In int32 BaseClass::Override(int32 modopt (FooModifier))"
20+
call void class [mscorlib]System.Console::WriteLine(string)
21+
ldc.i4.0
22+
ret
23+
}
24+
25+
.method public hidebysig virtual newslot instance int32 Override(int32 modopt (BarModifier))
26+
{
27+
ldstr "In int32 BaseClass::Override(int32 modopt (BarModifier))"
28+
call void class [mscorlib]System.Console::WriteLine(string)
29+
ldc.i4.1
30+
ret
31+
}
32+
33+
.method public hidebysig virtual newslot instance int32 Override(int32 modreq (FooModifier))
34+
{
35+
ldstr "In int32 BaseClass::Override(int32 modreq (FooModifier))"
36+
call void class [mscorlib]System.Console::WriteLine(string)
37+
ldc.i4.2
38+
ret
39+
}
40+
41+
.method public hidebysig virtual newslot instance int32 Override(int32)
42+
{
43+
ldstr "In int32 BaseClass::Override(int32)"
44+
call void class [mscorlib]System.Console::WriteLine(string)
45+
ldc.i4.3
46+
ret
47+
}
48+
49+
.method public hidebysig virtual newslot instance int32 modopt (FooModifier) Override(int32)
50+
{
51+
ldstr "In int32 modopt (FooModifier) BaseClass::Override(int32)"
52+
call void class [mscorlib]System.Console::WriteLine(string)
53+
ldc.i4.4
54+
ret
55+
}
56+
57+
.method public hidebysig virtual newslot instance int32 Override(int32 modopt (FooModifier)[])
58+
{
59+
ldstr "In int32 BaseClass::Override(int32 modopt (FooModifier)[])"
60+
call void class [mscorlib]System.Console::WriteLine(string)
61+
ldc.i4.5
62+
ret
63+
}
64+
65+
.method public hidebysig virtual newslot instance int32 Override(int32 modopt (BarModifier)[])
66+
{
67+
ldstr "In int32 BaseClass::Override(int32 modopt (BarModifier)[])"
68+
call void class [mscorlib]System.Console::WriteLine(string)
69+
ldc.i4.6
70+
ret
71+
}
72+
73+
}
74+
75+
.class public DerivedClass extends BaseClass
76+
{
77+
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
78+
{
79+
ldarg.0
80+
call instance void BaseClass::.ctor()
81+
ret
82+
}
83+
84+
.method public hidebysig virtual instance int32 Override(int32 modopt (FooModifier))
85+
{
86+
ldstr "In int32 DerivedClass::Override(int32 modopt (FooModifier))"
87+
call void class [mscorlib]System.Console::WriteLine(string)
88+
ldc.i4 100
89+
ret
90+
}
91+
92+
.method public hidebysig virtual instance int32 Override(int32 modopt (BarModifier))
93+
{
94+
ldstr "In int32 DerivedClass::Override(int32 modopt (BarModifier))"
95+
call void class [mscorlib]System.Console::WriteLine(string)
96+
ldc.i4 101
97+
ret
98+
}
99+
100+
.method public hidebysig virtual instance int32 Override(int32 modreq (FooModifier))
101+
{
102+
ldstr "In int32 DerivedClass::Override(int32 modreq (FooModifier))"
103+
call void class [mscorlib]System.Console::WriteLine(string)
104+
ldc.i4 102
105+
ret
106+
}
107+
108+
.method public hidebysig virtual instance int32 Override(int32)
109+
{
110+
ldstr "In int32 DerivedClass::Override(int32)"
111+
call void class [mscorlib]System.Console::WriteLine(string)
112+
ldc.i4 103
113+
ret
114+
}
115+
116+
.method public hidebysig virtual instance int32 modopt (FooModifier) Override(int32)
117+
{
118+
ldstr "In int32 modopt (FooModifier) DerivedClass::Override(int32)"
119+
call void class [mscorlib]System.Console::WriteLine(string)
120+
ldc.i4 104
121+
ret
122+
}
123+
124+
.method public hidebysig virtual instance int32 Override(int32 modopt (FooModifier)[])
125+
{
126+
ldstr "In int32 DerivedClass::Override(int32 modopt (FooModifier)[])"
127+
call void class [mscorlib]System.Console::WriteLine(string)
128+
ldc.i4 105
129+
ret
130+
}
131+
132+
.method public hidebysig virtual instance int32 Override(int32 modopt (BarModifier)[])
133+
{
134+
ldstr "In int32 DerivedClass::Override(int32 modopt (BarModifier)[])"
135+
call void class [mscorlib]System.Console::WriteLine(string)
136+
ldc.i4 106
137+
ret
138+
}
139+
}
140+
141+
.class public GenericClass`1<T>
142+
{
143+
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
144+
{
145+
ldarg.0
146+
call instance void [mscorlib]System.Object::.ctor()
147+
ret
148+
}
149+
}
150+
151+
.class public value sequential GenericStruct`1<T> { .size 1 }
152+
153+
.class public Factory
154+
{
155+
.method public static hidebysig object TryAllocWithModifiedArray()
156+
{
157+
newobj void class GenericClass`1<int32 modopt (FooModifier)[]>::.ctor()
158+
ret
159+
}
160+
161+
.method public static hidebysig object TryBoxWithModifiedPointer()
162+
{
163+
.locals init (valuetype GenericStruct`1<int32 modreq (BarModifier)*[]>)
164+
ldloc 0
165+
box valuetype GenericStruct`1<int32 modreq (BarModifier)*[]>
166+
ret
167+
}
168+
}
169+
170+
.class public FooModifier { }
171+
.class public BarModifier { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
4+
<PropertyGroup>
5+
<AssemblyName>modifiersdata</AssemblyName>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<SchemaVersion>2.0</SchemaVersion>
8+
<ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
9+
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
10+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
11+
<OutputType>Library</OutputType>
12+
<CLRTestKind>BuildOnly</CLRTestKind>
13+
<CLRTestPriority>0</CLRTestPriority>
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
18+
<Visible>False</Visible>
19+
</CodeAnalysisDependentAssemblyPaths>
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<Compile Include="modifiersdata.il" />
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
28+
</ItemGroup>
29+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
30+
</Project>

0 commit comments

Comments
 (0)