Skip to content

Commit 5f96710

Browse files
committed
DefaultMethodEmitter.cs: When emitting a proxy method, be more careful in how we get the MethodInfo for the target method. The previous code would get the closed generic method on .Net 2.0, but the open generic method definition on .Net 4.0. Now, explicitly reference the type arguments of the generated method, so we always get the closed generic method. This fixes NH-2819, NH-2726.
1 parent 517e03e commit 5f96710

File tree

3 files changed

+48
-25
lines changed

3 files changed

+48
-25
lines changed

src/NHibernate/Proxy/DynamicProxy/DefaultMethodEmitter.cs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ public DefaultMethodEmitter(IArgumentHandler argumentHandler)
5353

5454
#region IMethodBodyEmitter Members
5555

56-
public void EmitMethodBody(ILGenerator IL, MethodInfo method, FieldInfo field)
56+
public void EmitMethodBody(MethodBuilder generatedMethod, MethodInfo method, FieldInfo field)
5757
{
58+
var IL = generatedMethod.GetILGenerator();
59+
5860
ParameterInfo[] parameters = method.GetParameters();
5961
IL.DeclareLocal(typeof (object[]));
6062
IL.DeclareLocal(typeof (InvocationInfo));
@@ -73,33 +75,17 @@ public void EmitMethodBody(ILGenerator IL, MethodInfo method, FieldInfo field)
7375

7476
IL.Emit(OpCodes.Ldarg_0);
7577

76-
for(int i=0; i<method.GetParameters().Length; i++)
78+
for (int i = 0; i < method.GetParameters().Length; i++)
7779
IL.Emit(OpCodes.Ldarg_S, (sbyte)(i + 1));
7880

7981
IL.Emit(OpCodes.Call, method);
8082
IL.Emit(OpCodes.Ret);
8183

8284
IL.MarkLabel(skipBaseCall);
8385

84-
// Push the 'this' pointer onto the stack
85-
IL.Emit(OpCodes.Ldarg_0);
86-
87-
// Push the MethodInfo onto the stack
88-
System.Type declaringType = method.DeclaringType;
89-
90-
IL.Emit(OpCodes.Ldtoken, method);
91-
if (declaringType.IsGenericType)
92-
{
93-
IL.Emit(OpCodes.Ldtoken, declaringType);
94-
IL.Emit(OpCodes.Call, getGenericMethodFromHandle);
95-
}
96-
else
97-
{
98-
IL.Emit(OpCodes.Call, getMethodFromHandle);
99-
}
100-
101-
IL.Emit(OpCodes.Castclass, typeof (MethodInfo));
102-
86+
// Push arguments for InvocationInfo constructor.
87+
IL.Emit(OpCodes.Ldarg_0); // 'this' pointer
88+
PushTargetMethodInfo(IL, generatedMethod, method);
10389
PushStackTrace(IL);
10490
PushGenericArguments(method, IL);
10591
_argumentHandler.PushArguments(parameters, IL, false);
@@ -172,6 +158,44 @@ private static OpCode GetStindInstruction(System.Type parameterType)
172158
return OpCodes.Stind_Ref;
173159
}
174160

161+
private static void PushTargetMethodInfo(ILGenerator IL, MethodBuilder generatedMethod, MethodInfo method)
162+
{
163+
if (method.IsGenericMethodDefinition)
164+
{
165+
// We want the generated code to load a MethodInfo instantiated with the
166+
// current generic type parameters. I.e.:
167+
// MethodInfo methodInfo = methodof(TheClass.TheMethod<T>(...)
168+
//
169+
// We need to instantiate the open generic method definition with the type
170+
// arguments from the generated method. Using the open method definition
171+
// directly works on .Net 2.0, which might be FW bug, but fails on 4.0.
172+
//
173+
// Equivalent pseudo-code:
174+
// MethodInfo mi = methodof(TheClass.TheMethod<>)
175+
// versus
176+
// MethodInfo mi = methodof(TheClass.TheMethod<T0>)
177+
var instantiatedMethod = method.MakeGenericMethod(generatedMethod.GetGenericArguments());
178+
IL.Emit(OpCodes.Ldtoken, instantiatedMethod);
179+
}
180+
else
181+
{
182+
IL.Emit(OpCodes.Ldtoken, method);
183+
}
184+
185+
System.Type declaringType = method.DeclaringType;
186+
if (declaringType.IsGenericType)
187+
{
188+
IL.Emit(OpCodes.Ldtoken, declaringType);
189+
IL.Emit(OpCodes.Call, getGenericMethodFromHandle);
190+
}
191+
else
192+
{
193+
IL.Emit(OpCodes.Call, getMethodFromHandle);
194+
}
195+
196+
IL.Emit(OpCodes.Castclass, typeof(MethodInfo));
197+
}
198+
175199
private void PushStackTrace(ILGenerator IL)
176200
{
177201
// NOTE: The stack trace has been disabled for performance reasons

src/NHibernate/Proxy/DynamicProxy/DefaultProxyMethodBuilder.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,10 @@ public void CreateProxiedMethod(FieldInfo field, MethodInfo method, TypeBuilder
6464
typeArgsBuilder[index].SetInterfaceConstraints(typeArgs[index].GetGenericParameterConstraints());
6565
}
6666
}
67-
68-
ILGenerator IL = methodBuilder.GetILGenerator();
67+
6968

7069
Debug.Assert(MethodBodyEmitter != null);
71-
MethodBodyEmitter.EmitMethodBody(IL, method, field);
70+
MethodBodyEmitter.EmitMethodBody(methodBuilder, method, field);
7271
}
7372

7473
#endregion

src/NHibernate/Proxy/DynamicProxy/IMethodBodyEmitter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ namespace NHibernate.Proxy.DynamicProxy
1313
{
1414
public interface IMethodBodyEmitter
1515
{
16-
void EmitMethodBody(ILGenerator IL, MethodInfo method, FieldInfo field);
16+
void EmitMethodBody(MethodBuilder generatedMethod, MethodInfo method, FieldInfo field);
1717
}
1818
}

0 commit comments

Comments
 (0)