Skip to content

Commit e5ac6c8

Browse files
committed
NH-3260 - Correct handle generic argument constraints.
1 parent 6a50cf7 commit e5ac6c8

File tree

8 files changed

+318
-24
lines changed

8 files changed

+318
-24
lines changed

src/NHibernate.Test/DynamicProxyTests/GenericMethodsTests/GenericMethodShouldBeProxied.cs

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void CanProxyGenericMethodWithInterfaceConstraint()
4747
var c = (MyClass)factory.CreateProxy(typeof(MyClass), new PassThroughInterceptor(new MyClass()), null);
4848
c.MethodWithInterfaceConstraint<IMyInterface>(new MyDerivedClass()).Should().Be(typeof(IMyInterface));
4949
}
50-
50+
5151
[Test]
5252
public void CanProxyGenericMethodWithReferenceTypeAndInterfaceConstraint()
5353
{
@@ -56,6 +56,160 @@ public void CanProxyGenericMethodWithReferenceTypeAndInterfaceConstraint()
5656
c.MethodWithReferenceTypeAndInterfaceConstraint<MyDerivedClass>().Should().Not.Be.Null();
5757
}
5858

59+
[Test]
60+
public void CanProxySelfCastingMethodWithGenericConstaint()
61+
{
62+
var factory = new ProxyFactory();
63+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
64+
c.As<MyGenericClass<int>>().Should().Not.Be.Null();
65+
}
66+
67+
[Test]
68+
public void CanProxySelfCastingMethodWithGenericConstaintBase()
69+
{
70+
var factory = new ProxyFactory();
71+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
72+
c.AsBase<MyGenericClass<int>>().Should().Not.Be.Null();
73+
}
74+
75+
[Test]
76+
public void CanProxySelfCastingMethodWithGenericInterfaceConstaint()
77+
{
78+
var factory = new ProxyFactory();
79+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
80+
c.AsInterface<MyGenericClass<int>>().Should().Not.Be.Null();
81+
}
82+
83+
[Test]
84+
public void CanProxySelfCastingMethodWithGenericInterfaceBaseConstaint()
85+
{
86+
var factory = new ProxyFactory();
87+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
88+
c.AsInterfaceBase<MyGenericClass<int>>().Should().Not.Be.Null();
89+
}
90+
91+
[Test]
92+
public void CanProxySelfCastingMethodWithGenericConstaint2()
93+
{
94+
var factory = new ProxyFactory();
95+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
96+
c.As2<MyGenericClass<object>>().Should().Not.Be.Null();
97+
}
98+
99+
[Test]
100+
public void CanProxySelfCastingMethodWithGenericConstaintBase2()
101+
{
102+
var factory = new ProxyFactory();
103+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
104+
c.AsBase2<MyGenericClass<object>>().Should().Not.Be.Null();
105+
}
106+
107+
[Test]
108+
public void CanProxySelfCastingMethodWithGenericInterfaceConstaint2()
109+
{
110+
var factory = new ProxyFactory();
111+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
112+
c.AsInterface2<MyGenericClass<object>>().Should().Not.Be.Null();
113+
}
114+
115+
[Test]
116+
public void CanProxySelfCastingMethodWithGenericInterfaceBaseConstaint2()
117+
{
118+
var factory = new ProxyFactory();
119+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
120+
c.AsInterfaceBase2<MyGenericClass<object>>().Should().Not.Be.Null();
121+
}
122+
123+
[Test]
124+
public void CanProxySelfCastingMethodWithGenericConstaint3()
125+
{
126+
var factory = new ProxyFactory();
127+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
128+
c.As3<MyGenericClass<object>, object>().Should().Not.Be.Null();
129+
}
130+
131+
[Test]
132+
public void CanProxySelfCastingMethodWithGenericConstaintBase3()
133+
{
134+
var factory = new ProxyFactory();
135+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
136+
c.AsBase3<MyGenericClassBase<object, object>, object>().Should().Not.Be.Null();
137+
}
138+
139+
[Test]
140+
public void CanProxySelfCastingMethodWithGenericInterfaceConstaint3()
141+
{
142+
var factory = new ProxyFactory();
143+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
144+
c.AsInterface3<MyGenericClass<object>, object>().Should().Not.Be.Null();
145+
}
146+
147+
[Test]
148+
public void CanProxySelfCastingMethodWithGenericInterfaceBaseConstaint3()
149+
{
150+
var factory = new ProxyFactory();
151+
var c = (MyGenericClass<int>) factory.CreateProxy(typeof (MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
152+
c.AsInterfaceBase3<MyGenericClass<object>, object>().Should().Not.Be.Null();
153+
}
154+
155+
[Test]
156+
public void CanProxySelfCastingMethodWithGenericConstaint4()
157+
{
158+
var factory = new ProxyFactory();
159+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
160+
c.As4<MyGenericClass<int>>().Should().Not.Be.Null();
161+
}
162+
163+
[Test]
164+
public void CanProxySelfCastingMethodWithGenericConstaintBase4()
165+
{
166+
var factory = new ProxyFactory();
167+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
168+
c.AsBase4<MyGenericClass<int>>().Should().Not.Be.Null();
169+
}
170+
171+
[Test]
172+
public void CanProxySelfCastingMethodWithGenericInterfaceConstaint4()
173+
{
174+
var factory = new ProxyFactory();
175+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
176+
c.AsInterface4<MyGenericClass<int>>().Should().Not.Be.Null();
177+
}
178+
179+
[Test]
180+
public void CanProxySelfCastingMethodWithGenericInterfaceBaseConstaint4()
181+
{
182+
var factory = new ProxyFactory();
183+
var c = (MyGenericClass<int>)factory.CreateProxy(typeof(MyGenericClass<int>), new PassThroughInterceptor(new MyGenericClass<int>()), null);
184+
c.AsInterfaceBase4<MyGenericClass<int>>().Should().Not.Be.Null();
185+
}
186+
187+
[Test]
188+
public void GenericTypeConstraint()
189+
{
190+
var type = typeof(MyGenericClass<int>);
191+
var method = type.GetMethod("As");
192+
193+
var genericArgument = method.GetGenericArguments()[0]; // TRequestedType : class, IMyGenericInterface<TId>
194+
var typeConstraint = genericArgument.GetGenericParameterConstraints()[0]; // MyGenericClass<TId>
195+
196+
Assert.That(typeConstraint, Is.EqualTo(typeof(MyGenericClass<>)));
197+
Assert.That(typeConstraint.GetGenericTypeDefinition(), Is.EqualTo(typeof(MyGenericClass<>)));
198+
}
199+
200+
[Test]
201+
public void GenericInterfaceConstraint()
202+
{
203+
var type = typeof(MyGenericClass<int>);
204+
var method = type.GetMethod("AsInterface");
205+
206+
var genericArgument = method.GetGenericArguments()[0]; // TRequestedType : class, IMyGenericInterface<TId>
207+
var typeConstraint = genericArgument.GetGenericParameterConstraints()[0]; // IMyGenericInterface<TId>
208+
var typeConstraintGenericArgument = typeConstraint.GetGenericArguments()[0]; // TId
209+
210+
Assert.That(typeConstraint.GetGenericTypeDefinition(), Is.EqualTo(typeof(IMyGenericInterface<>)));
211+
}
212+
59213
class MyDerivedClass : MyClass, IMyInterface
60214
{ }
61215
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace NHibernate.Test.DynamicProxyTests.GenericMethodsTests
22
{
3-
public interface IMyGenericInterface<TId>
3+
public interface IMyGenericInterface<TId> : IMyGenericInterfaceBase<TId>
44
{
55
}
66
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace NHibernate.Test.DynamicProxyTests.GenericMethodsTests
2+
{
3+
public interface IMyGenericInterfaceBase<TId>
4+
{
5+
}
6+
}
Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,65 @@
11
namespace NHibernate.Test.DynamicProxyTests.GenericMethodsTests
22
{
3-
public class MyGenericClass<TId> : IMyGenericInterface<TId>
3+
public class MyGenericClass<TId> : MyGenericClassBase<TId, int>, IMyGenericInterface<TId>, IMyGenericInterface<MyGenericClass<TId>>
44
{
55
public virtual TRequestedType As<TRequestedType>() where TRequestedType : MyGenericClass<TId>
66
{
77
return this as TRequestedType;
88
}
99

10+
public virtual TRequestedType AsBase<TRequestedType>() where TRequestedType : MyGenericClassBase<TId, int>
11+
{
12+
return this as TRequestedType;
13+
}
14+
1015
public virtual TRequestedType AsInterface<TRequestedType>() where TRequestedType : class, IMyGenericInterface<TId>
1116
{
1217
return this as TRequestedType;
1318
}
19+
20+
public virtual TRequestedType AsInterfaceBase<TRequestedType>() where TRequestedType : class, IMyGenericInterfaceBase<TId>
21+
{
22+
return this as TRequestedType;
23+
}
24+
25+
public virtual TRequestedType As2<TRequestedType>() where TRequestedType : MyGenericClass<object>, new()
26+
{
27+
return new TRequestedType();
28+
}
29+
30+
public virtual TRequestedType AsBase2<TRequestedType>() where TRequestedType : MyGenericClassBase<object, int>, new()
31+
{
32+
return new TRequestedType();
33+
}
34+
35+
public virtual TRequestedType AsInterface2<TRequestedType>() where TRequestedType : class, IMyGenericInterface<object>, new()
36+
{
37+
return new TRequestedType();
38+
}
39+
40+
public virtual TRequestedType AsInterfaceBase2<TRequestedType>() where TRequestedType : class, IMyGenericInterfaceBase<object>, new()
41+
{
42+
return new TRequestedType();
43+
}
44+
45+
public virtual TRequestedType As3<TRequestedType,T>() where TRequestedType : MyGenericClass<T>, new()
46+
{
47+
return new TRequestedType();
48+
}
49+
50+
public virtual TRequestedType AsBase3<TRequestedType,T>() where TRequestedType : MyGenericClassBase<T, object>, new()
51+
{
52+
return new TRequestedType();
53+
}
54+
55+
public virtual TRequestedType AsInterface3<TRequestedType, T>() where TRequestedType : class, IMyGenericInterface<T>, new()
56+
{
57+
return new TRequestedType();
58+
}
59+
60+
public virtual TRequestedType AsInterfaceBase3<TRequestedType, T>() where TRequestedType : class, IMyGenericInterfaceBase<T>, new()
61+
{
62+
return new TRequestedType();
63+
}
1464
}
15-
}
65+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace NHibernate.Test.DynamicProxyTests.GenericMethodsTests
2+
{
3+
public class MyGenericClassBase<TId, T2>
4+
{
5+
public virtual TRequestedType As4<TRequestedType>() where TRequestedType : MyGenericClass<TId>
6+
{
7+
return this as TRequestedType;
8+
}
9+
10+
public virtual TRequestedType AsBase4<TRequestedType>() where TRequestedType : MyGenericClassBase<TId, int>
11+
{
12+
return this as TRequestedType;
13+
}
14+
15+
public virtual TRequestedType AsInterface4<TRequestedType>() where TRequestedType : class, IMyGenericInterface<TId>
16+
{
17+
return this as TRequestedType;
18+
}
19+
20+
public virtual TRequestedType AsInterfaceBase4<TRequestedType>() where TRequestedType : class, IMyGenericInterface<TId>
21+
{
22+
return this as TRequestedType;
23+
}
24+
}
25+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,11 @@
223223
<Compile Include="DriverTest\SqlClientDriverFixture.cs" />
224224
<Compile Include="DriverTest\SqlServerCeDriverFixture.cs" />
225225
<Compile Include="DynamicProxyTests\GenericMethodsTests\IMyGenericInterface.cs" />
226+
<Compile Include="DynamicProxyTests\GenericMethodsTests\IMyGenericInterfaceBase.cs" />
226227
<Compile Include="DynamicProxyTests\GenericMethodsTests\IMyInterface.cs" />
227228
<Compile Include="DynamicProxyTests\GenericMethodsTests\MyClass.cs" />
228229
<Compile Include="DynamicProxyTests\GenericMethodsTests\MyGenericClass.cs" />
230+
<Compile Include="DynamicProxyTests\GenericMethodsTests\MyGenericClassBase.cs" />
229231
<Compile Include="DynamicProxyTests\PeVerifyFixture.cs" />
230232
<Compile Include="DynamicProxyTests\GenericMethodsTests\GenericMethodShouldBeProxied.cs" />
231233
<Compile Include="DynamicProxyTests\InterfaceProxySerializationTests\IMyProxy.cs" />

src/NHibernate/Proxy/DynamicProxy/DefaultProxyMethodBuilder.cs

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,91 @@ private static MethodBuilder GenerateMethodSignature(string name, MethodInfo met
5050

5151
if (typeArgs.Length > 0)
5252
{
53-
var typeNames = new List<string>();
54-
55-
for (int index = 0; index < typeArgs.Length; index++)
56-
{
57-
typeNames.Add(string.Format("T{0}", index));
58-
}
59-
60-
var typeArgsBuilder = methodBuilder.DefineGenericParameters(typeNames.ToArray());
53+
var typeNames = GenerateTypeNames(typeArgs.Length);
54+
var typeArgBuilders = methodBuilder.DefineGenericParameters(typeNames);
6155

6256
for (int index = 0; index < typeArgs.Length; index++)
6357
{
6458
// Copy generic parameter attributes (Covariant, Contravariant, ReferenceTypeConstraint,
6559
// NotNullableValueTypeConstraint, DefaultConstructorConstraint).
66-
typeArgsBuilder[index].SetGenericParameterAttributes(typeArgs[index].GenericParameterAttributes);
60+
var typeArgBuilder = typeArgBuilders[index];
61+
var typeArg = typeArgs[index];
62+
63+
typeArgBuilder.SetGenericParameterAttributes(typeArg.GenericParameterAttributes);
6764

6865
// Copy generic parameter constraints (class and interfaces).
69-
var typeConstraints = typeArgs[index].GetGenericParameterConstraints();
66+
var typeConstraints = typeArg.GetGenericParameterConstraints()
67+
.Select(x => ResolveTypeConstraint(method, x))
68+
.ToArray();
7069

7170
var baseTypeConstraint = typeConstraints.SingleOrDefault(x => x.IsClass);
72-
typeArgsBuilder[index].SetBaseTypeConstraint(baseTypeConstraint);
71+
typeArgBuilder.SetBaseTypeConstraint(baseTypeConstraint);
7372

7473
var interfaceTypeConstraints = typeConstraints.Where(x => !x.IsClass).ToArray();
75-
typeArgsBuilder[index].SetInterfaceConstraints(interfaceTypeConstraints);
74+
typeArgBuilder.SetInterfaceConstraints(interfaceTypeConstraints);
7675
}
7776
}
7877
return methodBuilder;
7978
}
8079

80+
private static System.Type ResolveTypeConstraint(MethodInfo method, System.Type typeConstraint)
81+
{
82+
if (typeConstraint != null && typeConstraint.IsGenericType)
83+
{
84+
var declaringType = method.DeclaringType;
85+
if (declaringType != null && declaringType.IsGenericType)
86+
{
87+
return BuildTypeConstraint(typeConstraint, declaringType);
88+
}
89+
}
90+
91+
return typeConstraint;
92+
}
93+
94+
private static System.Type BuildTypeConstraint(System.Type typeConstraint, System.Type declaringType)
95+
{
96+
var constraintGenericArguments = typeConstraint.GetGenericArguments();
97+
var declaringTypeGenericArguments = declaringType.GetGenericArguments();
98+
99+
var parametersMap = declaringType
100+
.GetGenericTypeDefinition()
101+
.GetGenericArguments()
102+
.ToDictionary(x => x, x => declaringTypeGenericArguments[x.GenericParameterPosition]);
103+
104+
var args = new System.Type[constraintGenericArguments.Length];
105+
var make = false;
106+
for (int index = 0; index < constraintGenericArguments.Length; index++)
107+
{
108+
var genericArgument = constraintGenericArguments[index];
109+
System.Type result;
110+
if (parametersMap.TryGetValue(genericArgument, out result))
111+
{
112+
make = true;
113+
}
114+
else
115+
{
116+
result = genericArgument;
117+
}
118+
args[index] = result;
119+
}
120+
if (make)
121+
{
122+
return typeConstraint.GetGenericTypeDefinition().MakeGenericType(args);
123+
}
124+
125+
return typeConstraint;
126+
}
127+
128+
private static string[] GenerateTypeNames(int count)
129+
{
130+
var result = new string[count];
131+
for (int index = 0; index < count; index++)
132+
{
133+
result[index] = string.Format("T{0}", index);
134+
}
135+
return result;
136+
}
137+
81138
public void CreateProxiedMethod(FieldInfo field, MethodInfo method, TypeBuilder typeBuilder)
82139
{
83140
var callbackMethod = GenerateMethodSignature(method.Name + "_callback", method, typeBuilder);

0 commit comments

Comments
 (0)