From 53154a77dbe8469542238e9d63ed944aca34939f Mon Sep 17 00:00:00 2001 From: Duncan M Date: Fri, 19 Oct 2018 16:17:57 -0600 Subject: [PATCH 1/5] GH-1881 - Fix interface method attributes when generating proxies --- .../NHSpecificTest/GH1881/TestFixture.cs | 33 +++++++++++++++++++ .../Proxy/DynamicProxy/ProxyFactory.cs | 4 +-- .../Proxy/NHibernateProxyBuilder.cs | 2 +- src/NHibernate/Proxy/ProxyBuilderHelper.cs | 19 ++++++++++- 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/NHibernate.Test/NHSpecificTest/GH1881/TestFixture.cs diff --git a/src/NHibernate.Test/NHSpecificTest/GH1881/TestFixture.cs b/src/NHibernate.Test/NHSpecificTest/GH1881/TestFixture.cs new file mode 100644 index 00000000000..96c454cf201 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH1881/TestFixture.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Reflection; +using NHibernate.Collection; +using NHibernate.Collection.Generic; +using NHibernate.Proxy.DynamicProxy; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH1881 +{ + [TestFixture] + [Obsolete] + public class TestFixture + { + [Test] + public void InterfacesShouldBeImplementedExplicitlyOnProxies() + { + var proxy = new ProxyFactory().CreateProxy(typeof(PersistentGenericBag), null, typeof(ILazyInitializedCollection)); + Assert.That(proxy, Is.Not.Null); + + foreach (var method in proxy.GetType().GetMethods().Where(m => m.DeclaringType == typeof(ILazyInitializedCollection))) + { + // These attributes are what .NET uses for explicitly implemented interface methods + Assert.That(method.Attributes, Is.EqualTo(MethodAttributes.Private | + MethodAttributes.Final | + MethodAttributes.Virtual | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.SpecialName)); + } + } + } +} diff --git a/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs b/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs index ad0a79a60c3..75afc401aac 100644 --- a/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs +++ b/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs @@ -118,13 +118,13 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, IReadOnlyCollecti // Provide a custom implementation of ISerializable // instead of redirecting it back to the interceptor - foreach (MethodInfo method in ProxyBuilderHelper.GetProxiableMethods(baseType, interfaces).Where(method => method.DeclaringType != typeof(ISerializable))) + foreach (MethodInfo method in ProxyBuilderHelper.GetProxiableMethods(parentType, interfaces).Where(method => method.DeclaringType != typeof(ISerializable))) { ProxyMethodBuilder.CreateProxiedMethod(interceptorField, method, typeBuilder); } // Make the proxy serializable - AddSerializationSupport(baseType, baseInterfaces, typeBuilder, interceptorField, defaultConstructor); + AddSerializationSupport(parentType, baseInterfaces, typeBuilder, interceptorField, defaultConstructor); TypeInfo proxyType = typeBuilder.CreateTypeInfo(); ProxyAssemblyBuilder.Save(assemblyBuilder); diff --git a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs index 8514ac7dc14..345709d58b0 100644 --- a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs +++ b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs @@ -394,7 +394,7 @@ private static void EmitCallBaseIfLazyInitializerIsNull( IL.Emit(OpCodes.Bne_Un, skipBaseCall); IL.Emit(OpCodes.Ldarg_0); - EmitCallMethod(IL, OpCodes.Call, method); + EmitCallMethod(IL, method.DeclaringType.IsInterface ? OpCodes.Callvirt : OpCodes.Call, method); IL.Emit(OpCodes.Ret); IL.MarkLabel(skipBaseCall); diff --git a/src/NHibernate/Proxy/ProxyBuilderHelper.cs b/src/NHibernate/Proxy/ProxyBuilderHelper.cs index 94af19db95d..df32f3d06b4 100644 --- a/src/NHibernate/Proxy/ProxyBuilderHelper.cs +++ b/src/NHibernate/Proxy/ProxyBuilderHelper.cs @@ -137,7 +137,24 @@ internal static MethodBuilder GetObjectDataMethodBuilder(TypeBuilder typeBuilder internal static MethodBuilder GenerateMethodSignature(string name, MethodInfo method, TypeBuilder typeBuilder) { //TODO: Should we use attributes of base method? - var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual; + MethodAttributes methodAttributes; + if (method.DeclaringType.IsInterface) + { + // These are the attributes used for an explicit interface method implementation in .NET. + methodAttributes = + MethodAttributes.Private | + MethodAttributes.Final | + MethodAttributes.Virtual | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.SpecialName; + // .NET uses an expanded name for explicit interface implementation methods. + name = typeBuilder.FullName + "." + method.DeclaringType.FullName + "." + name; + } + else + { + methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual; + } if (method.IsSpecialName) methodAttributes |= MethodAttributes.SpecialName; From 27decebf96256da718e1e8019f6f9d632d3c40ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Tue, 23 Oct 2018 19:22:03 +0200 Subject: [PATCH 2/5] fixup! GH-1881 - Fix interface method attributes when generating proxies Fix a broken test --- src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs b/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs index bce91d9424b..8b20a8c8b2f 100644 --- a/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs +++ b/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs @@ -53,6 +53,15 @@ public object Intercept(InvocationInfo info) { return GetHashCode(); } + else if ("Equals".Equals(methodName)) + { + // Call the base object Equals. Taken from https://stackoverflow.com/a/14415506/1178314 + var method = typeof(object).GetMethod("Equals", new[] { typeof(object) }); + var ftn = method.MethodHandle.GetFunctionPointer(); + var func = (Func)Activator.CreateInstance(typeof(Func), info.Target, ftn); + + return func(info.Arguments[0]); + } return null; } From 402f14cb4d51dfbc0ff1853a913c59f877eccc57 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 30 Oct 2018 16:07:37 +1300 Subject: [PATCH 3/5] fixup! GH-1881 - Fix interface method attributes when generating proxies --- src/NHibernate/Proxy/NHibernateProxyBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs index 9d42a1590e4..0be16616ac2 100644 --- a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs +++ b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs @@ -398,6 +398,7 @@ private static void EmitCallBaseIfLazyInitializerIsNull( if (method.DeclaringType.IsInterface && method.DeclaringType.IsAssignableFrom(parentType)) { + Delegate var interfaceMap = parentType.GetInterfaceMap(method.DeclaringType); var methodIndex = Array.IndexOf(interfaceMap.InterfaceMethods, method); method = interfaceMap.TargetMethods[methodIndex]; @@ -462,7 +463,7 @@ private static void EmitCallBaseIfLazyInitializerIsNull( */ IL.Emit(OpCodes.Ldarg_0); - EmitCallMethod(IL, method.DeclaringType.IsInterface ? OpCodes.Callvirt : OpCodes.Call, method); + EmitCallMethod(IL, OpCodes.Call, method); IL.Emit(OpCodes.Ret); } From 5c87d9ae0dbe42350d41461cad2d972e4c821c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Tue, 30 Oct 2018 15:13:50 +0100 Subject: [PATCH 4/5] fixup! GH-1881 - Fix interface method attributes when generating proxies Rmove some undue changes --- src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs | 2 +- src/NHibernate/Proxy/NHibernateProxyBuilder.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs b/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs index 75afc401aac..c57e4e28e7a 100644 --- a/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs +++ b/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs @@ -124,7 +124,7 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, IReadOnlyCollecti } // Make the proxy serializable - AddSerializationSupport(parentType, baseInterfaces, typeBuilder, interceptorField, defaultConstructor); + AddSerializationSupport(baseType, baseInterfaces, typeBuilder, interceptorField, defaultConstructor); TypeInfo proxyType = typeBuilder.CreateTypeInfo(); ProxyAssemblyBuilder.Save(assemblyBuilder); diff --git a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs index 0be16616ac2..607e548c4f0 100644 --- a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs +++ b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs @@ -398,7 +398,6 @@ private static void EmitCallBaseIfLazyInitializerIsNull( if (method.DeclaringType.IsInterface && method.DeclaringType.IsAssignableFrom(parentType)) { - Delegate var interfaceMap = parentType.GetInterfaceMap(method.DeclaringType); var methodIndex = Array.IndexOf(interfaceMap.InterfaceMethods, method); method = interfaceMap.TargetMethods[methodIndex]; From 61ed84f95bfc4902df8e5629b84618d39e324234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Tue, 30 Oct 2018 15:16:27 +0100 Subject: [PATCH 5/5] fixup! GH-1881 - Fix interface method attributes when generating proxies Simplify broken test fix --- src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs b/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs index 8b20a8c8b2f..69d306147cf 100644 --- a/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs +++ b/src/NHibernate.Test/DynamicEntity/DataProxyHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Runtime.CompilerServices; using NHibernate.Proxy.DynamicProxy; namespace NHibernate.Test.DynamicEntity @@ -55,12 +56,8 @@ public object Intercept(InvocationInfo info) } else if ("Equals".Equals(methodName)) { - // Call the base object Equals. Taken from https://stackoverflow.com/a/14415506/1178314 - var method = typeof(object).GetMethod("Equals", new[] { typeof(object) }); - var ftn = method.MethodHandle.GetFunctionPointer(); - var func = (Func)Activator.CreateInstance(typeof(Func), info.Target, ftn); - - return func(info.Arguments[0]); + // Redo the base object Equals implementation. + return RuntimeHelpers.Equals(info.Target, info.Arguments[0]); } return null; }