Skip to content

Generate a correct proxy for interfaces #1728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/NHibernate.Test/StaticProxyTest/StaticProxyFactoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,34 @@ public void VerifyProxyForClassWithInternalInterface()
#endif
}

[Test]
public void VerifyProxyForClassWithAdditionalInterface()
{
var factory = new StaticProxyFactory();
factory.PostInstantiate(
typeof(PublicInterfaceTestClass).FullName,
typeof(PublicInterfaceTestClass),
// By way of the "proxy" attribute on the "class" mapping, an interface to use for the
// lazy entity load proxy instead of the persistentClass can be specified. This is "translated" into
// having an additional interface in the interface list, instead of just having INHibernateProxy.
// (Quite a loosy semantic...)
new HashSet<System.Type> {typeof(INHibernateProxy), typeof(IPublic)},
null, null, null);

#if NETFX
VerifyGeneratedAssembly(
() =>
{
#endif
var proxy = factory.GetProxy(1, null);
Assert.That(proxy, Is.Not.Null);
Assert.That(proxy, Is.InstanceOf<IPublic>());
Assert.That(proxy, Is.Not.InstanceOf<PublicInterfaceTestClass>());
#if NETFX
});
#endif
}

[Test]
public void CanSerializeFieldInterceptorProxy()
{
Expand Down
70 changes: 50 additions & 20 deletions src/NHibernate/Proxy/NHibernateProxyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ public NHibernateProxyBuilder(MethodInfo getIdentifierMethod, MethodInfo setIden

public TypeInfo CreateProxyType(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces)
{
var typeName = $"{baseType.Name}Proxy";
System.Type interfaceType = null;
if (baseType == typeof(object))
{
// Mapping option "proxy" allows to ask for using an interface, which switches the base type to object
// and adds the interface to base interfaces set.
// Avoids using object for naming the proxy, as otherwise all entities using the "proxy" option for
// specifying an interface would have their proxies sharing the same full name.
interfaceType = baseInterfaces.FirstOrDefault(i => i != typeof(INHibernateProxy));
}
var typeName = $"{(interfaceType ?? baseType).Name}Proxy";
var assemblyName = $"{typeName}Assembly";
var moduleName = $"{typeName}Module";

Expand Down Expand Up @@ -77,7 +86,7 @@ public TypeInfo CreateProxyType(System.Type baseType, IReadOnlyCollection<System
// Provide a custom implementation of ISerializable instead of redirecting it back to the interceptor
foreach (var method in ProxyBuilderHelper.GetProxiableMethods(baseType, interfaces.Except(new[] {typeof(ISerializable)})))
{
CreateProxiedMethod(typeBuilder, method, lazyInitializerField);
CreateProxiedMethod(typeBuilder, method, lazyInitializerField, parentType);
}

ProxyBuilderHelper.MakeProxySerializable(typeBuilder);
Expand All @@ -91,19 +100,20 @@ public TypeInfo CreateProxyType(System.Type baseType, IReadOnlyCollection<System
return proxyType;
}

private void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
private void CreateProxiedMethod(
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
{
if (method == NHibernateProxyTypeLazyInitializerProperty.GetMethod)
{
ImplementGetLazyInitializer(typeBuilder, method, lazyInitializerField);
}
else if (method == _getIdentifierMethod)
{
ImplementGetIdentifier(typeBuilder, method, lazyInitializerField);
ImplementGetIdentifier(typeBuilder, method, lazyInitializerField, parentType);
}
else if (method == _setIdentifierMethod)
{
ImplementSetIdentifier(typeBuilder, method, lazyInitializerField);
ImplementSetIdentifier(typeBuilder, method, lazyInitializerField, parentType);
}
else if (!_overridesEquals && method.Name == "Equals" && method.GetBaseDefinition() == typeof(object).GetMethod("Equals", new[] {typeof(object)}))
{
Expand All @@ -115,11 +125,11 @@ private void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, Fie
}
else if (_componentIdType != null && _componentIdType.IsMethodOf(method))
{
ImplementCallMethodOnEmbeddedComponentId(typeBuilder, method, lazyInitializerField);
ImplementCallMethodOnEmbeddedComponentId(typeBuilder, method, lazyInitializerField, parentType);
}
else
{
ImplementCallMethodOnImplementation(typeBuilder, method, lazyInitializerField);
ImplementCallMethodOnImplementation(typeBuilder, method, lazyInitializerField, parentType);
}
}

Expand Down Expand Up @@ -217,7 +227,8 @@ private static void ImplementGetLazyInitializer(TypeBuilder typeBuilder, MethodI
typeBuilder.DefineMethodOverride(getMethod, method);
}

private static void ImplementGetIdentifier(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
private static void ImplementGetIdentifier(
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
{
/*
get
Expand All @@ -231,7 +242,7 @@ private static void ImplementGetIdentifier(TypeBuilder typeBuilder, MethodInfo m

var IL = methodOverride.GetILGenerator();

EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);

IL.Emit(OpCodes.Ldarg_0);
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
Expand All @@ -242,10 +253,11 @@ private static void ImplementGetIdentifier(TypeBuilder typeBuilder, MethodInfo m
typeBuilder.DefineMethodOverride(methodOverride, method);
}

private static void ImplementSetIdentifier(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
private static void ImplementSetIdentifier(
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
{
/*
set
set
{
if (this.__lazyInitializer == null)
return base.set_<Identifier>(value);
Expand All @@ -258,7 +270,7 @@ private static void ImplementSetIdentifier(TypeBuilder typeBuilder, MethodInfo m
var methodOverride = ProxyBuilderHelper.GenerateMethodSignature(method.Name, method, typeBuilder);
var IL = methodOverride.GetILGenerator();

EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);

IL.Emit(OpCodes.Ldarg_0);
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
Expand All @@ -278,7 +290,8 @@ private static void ImplementSetIdentifier(TypeBuilder typeBuilder, MethodInfo m
typeBuilder.DefineMethodOverride(methodOverride, method);
}

private static void ImplementCallMethodOnEmbeddedComponentId(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
private static void ImplementCallMethodOnEmbeddedComponentId(
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
{
/*
if (this.__lazyInitializer == null)
Expand All @@ -289,7 +302,7 @@ private static void ImplementCallMethodOnEmbeddedComponentId(TypeBuilder typeBui

var IL = methodOverride.GetILGenerator();

EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);

IL.Emit(OpCodes.Ldarg_0);
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
Expand All @@ -301,29 +314,46 @@ private static void ImplementCallMethodOnEmbeddedComponentId(TypeBuilder typeBui
typeBuilder.DefineMethodOverride(methodOverride, method);
}

private static void ImplementCallMethodOnImplementation(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
private static void ImplementCallMethodOnImplementation(
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
{
/*
if (this.__lazyInitializer == null)
return base.<Method>(args..);
return this.__lazyInitializer.GetImplementation().<Method>(args..)
return this.__lazyInitializer.GetImplementation().<Method>(args..)
*/
var methodOverride = ProxyBuilderHelper.GenerateMethodSignature(method.Name, method, typeBuilder);

var IL = methodOverride.GetILGenerator();

EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);

EmitCallImplementation(IL, method, lazyInitializerField);
IL.Emit(OpCodes.Ret);

typeBuilder.DefineMethodOverride(methodOverride, method);
}

private static void EmitCallBaseIfLazyInitializerIsNull(ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField)
private static void EmitCallBaseIfLazyInitializerIsNull(
ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
{
//if (this.__lazyInitializer == null)
// return base.<Method>(args..)
/*
<if (method.DeclaringType.IsAssignableFrom(parentType))
{>
if (this.__lazyInitializer == null)
return base.<method>(args..)
<}>
*/
if (!method.DeclaringType.IsAssignableFrom(parentType))
// The proxy does not derive from a type implementing the method, do not attempt
// calling its base. In such case, the lazy initializer is never null.
return;

// When deriving from the entity class, the entity class constructor may trigger
// virtual calls accessing the proxy state before its own constructor has a chance
// to initialize it. So although lazyInitializer is never supplied as null to the
// proxy constructor, we must guard nonetheless against it being null during base
// constructor call.

IL.Emit(OpCodes.Ldarg_0);
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
Expand Down