Skip to content

Commit 285a794

Browse files
committed
Fix attempt to call base method for abstract classes
Previously it was failing PEVerify similarly to #1728 but for abstract classes (this is possible in case of a polymorphic entities). Unlike interfaces the abstract base class can go into a situation when lazy initializer is not yet available, eg. code in a constructor.
1 parent 5c18452 commit 285a794

File tree

2 files changed

+63
-11
lines changed

2 files changed

+63
-11
lines changed

src/NHibernate.Test/StaticProxyTest/StaticProxyFactoryFixture.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ public class PublicInterfaceTestClass : IPublic
4242
public virtual int Id { get; set; }
4343
}
4444

45+
[Serializable]
46+
public abstract class AbstractTestClass : IPublic
47+
{
48+
protected AbstractTestClass()
49+
{
50+
Id = -1;
51+
}
52+
53+
public abstract int Id { get; set; }
54+
}
55+
4556
[Serializable]
4657
public class SimpleTestClass
4758
{
@@ -124,7 +135,7 @@ public void VerifyProxyForClassWithAdditionalInterface()
124135
// lazy entity load proxy instead of the persistentClass can be specified. This is "translated" into
125136
// having an additional interface in the interface list, instead of just having INHibernateProxy.
126137
// (Quite a loosy semantic...)
127-
new HashSet<System.Type> {typeof(INHibernateProxy), typeof(IPublic)},
138+
new HashSet<System.Type> { typeof(INHibernateProxy), typeof(IPublic) },
128139
null, null, null);
129140

130141
#if NETFX
@@ -141,6 +152,30 @@ public void VerifyProxyForClassWithAdditionalInterface()
141152
#endif
142153
}
143154

155+
[Test]
156+
public void VerifyProxyForAbstractClass()
157+
{
158+
var factory = new StaticProxyFactory();
159+
factory.PostInstantiate(
160+
typeof(AbstractTestClass).FullName,
161+
typeof(AbstractTestClass),
162+
new HashSet<System.Type> { typeof(INHibernateProxy) },
163+
null, null, null);
164+
165+
#if NETFX
166+
VerifyGeneratedAssembly(
167+
() =>
168+
{
169+
#endif
170+
var proxy = factory.GetProxy(1, null);
171+
Assert.That(proxy, Is.Not.Null);
172+
Assert.That(proxy, Is.InstanceOf<IPublic>());
173+
Assert.That(proxy, Is.InstanceOf<AbstractTestClass>());
174+
#if NETFX
175+
});
176+
#endif
177+
}
178+
144179
[Test]
145180
public void InitializedProxyStaysInitializedAfterDeserialization()
146181
{

src/NHibernate/Proxy/NHibernateProxyBuilder.cs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -369,16 +369,14 @@ private static void EmitCallBaseIfLazyInitializerIsNull(
369369
ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
370370
{
371371
/*
372-
<if (method.DeclaringType.IsAssignableFrom(parentType))
373-
{>
374372
if (this.__lazyInitializer == null)
375-
return base.<method>(args..)
373+
<if (method.IsAbstract)
374+
{>
375+
return default;
376+
<} else {>
377+
return base.<method>(args..);
376378
<}>
377379
*/
378-
if (!method.DeclaringType.IsAssignableFrom(parentType))
379-
// The proxy does not derive from a type implementing the method, do not attempt
380-
// calling its base. In such case, the lazy initializer is never null.
381-
return;
382380

383381
// When deriving from the entity class, the entity class constructor may trigger
384382
// virtual calls accessing the proxy state before its own constructor has a chance
@@ -393,9 +391,28 @@ private static void EmitCallBaseIfLazyInitializerIsNull(
393391
IL.Emit(OpCodes.Ldnull);
394392
IL.Emit(OpCodes.Bne_Un, skipBaseCall);
395393

396-
IL.Emit(OpCodes.Ldarg_0);
397-
EmitCallMethod(IL, OpCodes.Call, method);
398-
IL.Emit(OpCodes.Ret);
394+
if (method.IsAbstract)
395+
{
396+
if (!method.ReturnType.IsValueType)
397+
{
398+
IL.Emit(OpCodes.Ldnull);
399+
}
400+
else if (method.ReturnType != typeof(void))
401+
{
402+
var local = IL.DeclareLocal(method.ReturnType);
403+
IL.Emit(OpCodes.Ldloca, local);
404+
IL.Emit(OpCodes.Initobj, method.ReturnType);
405+
IL.Emit(OpCodes.Ldloc, local);
406+
}
407+
408+
IL.Emit(OpCodes.Ret);
409+
}
410+
else
411+
{
412+
IL.Emit(OpCodes.Ldarg_0);
413+
EmitCallMethod(IL, OpCodes.Call, method);
414+
IL.Emit(OpCodes.Ret);
415+
}
399416

400417
IL.MarkLabel(skipBaseCall);
401418
}

0 commit comments

Comments
 (0)