Skip to content

Commit c341f30

Browse files
committed
GH-1881 - Fix interface method attributes when generating proxies
1 parent baee90e commit c341f30

File tree

4 files changed

+59
-2
lines changed

4 files changed

+59
-2
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
using NHibernate.Collection;
5+
using NHibernate.Collection.Generic;
6+
using NHibernate.Proxy.DynamicProxy;
7+
using NUnit.Framework;
8+
9+
namespace NHibernate.Test.NHSpecificTest.GH1881
10+
{
11+
[TestFixture]
12+
[Obsolete]
13+
public class TestFixture
14+
{
15+
[Test]
16+
public void InterfacesShouldBeImplementedExplicitlyOnProxies()
17+
{
18+
var proxy = new ProxyFactory().CreateProxy(typeof(PersistentGenericBag<object>), null, typeof(ILazyInitializedCollection));
19+
Assert.That(proxy, Is.Not.Null);
20+
21+
foreach (var method in proxy.GetType().GetMethods().Where(m => m.DeclaringType == typeof(ILazyInitializedCollection)))
22+
{
23+
// These attributes are what .NET uses for explicitly implemented interface methods
24+
Assert.That(method.Attributes, Is.EqualTo(MethodAttributes.Private |
25+
MethodAttributes.Final |
26+
MethodAttributes.Virtual |
27+
MethodAttributes.HideBySig |
28+
MethodAttributes.NewSlot |
29+
MethodAttributes.SpecialName));
30+
}
31+
}
32+
}
33+
}

src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ public System.Type CreateProxyType(System.Type baseType, params System.Type[] in
6868
{
6969
if (baseType == null) throw new ArgumentNullException(nameof(baseType));
7070

71+
// Also proxy the methods of [object] if creating a proxy for an interface
72+
if (baseType.IsInterface)
73+
{
74+
interfaces = new [] { baseType }.Concat(interfaces ?? new System.Type[0]).ToArray();
75+
baseType = typeof(object);
76+
}
77+
7178
var typeFactory = _cache.GetOrAdd(
7279
new ProxyCacheEntry(baseType, interfaces),
7380
k => CreateUncachedProxyType(k.BaseType, k.Interfaces));

src/NHibernate/Proxy/NHibernateProxyBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ private static void EmitCallBaseIfLazyInitializerIsNull(
394394
IL.Emit(OpCodes.Bne_Un, skipBaseCall);
395395

396396
IL.Emit(OpCodes.Ldarg_0);
397-
EmitCallMethod(IL, OpCodes.Call, method);
397+
EmitCallMethod(IL, method.DeclaringType.IsInterface ? OpCodes.Callvirt : OpCodes.Call, method);
398398
IL.Emit(OpCodes.Ret);
399399

400400
IL.MarkLabel(skipBaseCall);

src/NHibernate/Proxy/ProxyBuilderHelper.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,24 @@ internal static MethodBuilder GetObjectDataMethodBuilder(TypeBuilder typeBuilder
137137
internal static MethodBuilder GenerateMethodSignature(string name, MethodInfo method, TypeBuilder typeBuilder)
138138
{
139139
//TODO: Should we use attributes of base method?
140-
var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
140+
MethodAttributes methodAttributes;
141+
if (method.DeclaringType.IsInterface)
142+
{
143+
// These are the attributes used for an explicit interface method implementation in .NET.
144+
methodAttributes =
145+
MethodAttributes.Private |
146+
MethodAttributes.Final |
147+
MethodAttributes.Virtual |
148+
MethodAttributes.HideBySig |
149+
MethodAttributes.NewSlot |
150+
MethodAttributes.SpecialName;
151+
// .NET uses an expanded name for explicit interface implementation methods.
152+
name = typeBuilder.FullName + "." + method.DeclaringType.FullName + "." + name;
153+
}
154+
else
155+
{
156+
methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
157+
}
141158

142159
if (method.IsSpecialName)
143160
methodAttributes |= MethodAttributes.SpecialName;

0 commit comments

Comments
 (0)