Skip to content

Commit 90e75b9

Browse files
Generate a correct proxy for interfaces
Static proxies generated for interfaces were incorrectly attempting to call the base "interface implementation". And they were all sharing the same type name.
1 parent dc71c7d commit 90e75b9

File tree

2 files changed

+78
-20
lines changed

2 files changed

+78
-20
lines changed

src/NHibernate.Test/StaticProxyTest/StaticProxyFactoryFixture.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,34 @@ public void VerifyProxyForClassWithInternalInterface()
107107
#endif
108108
}
109109

110+
[Test]
111+
public void VerifyProxyForClassWithAdditionalInterface()
112+
{
113+
var factory = new StaticProxyFactory();
114+
factory.PostInstantiate(
115+
typeof(PublicInterfaceTestClass).FullName,
116+
typeof(PublicInterfaceTestClass),
117+
// By way of the "proxy" attribute on the "class" mapping, an interface to use for the
118+
// lazy entity load proxy instead of the persistentClass can be specified. This is "translated" into
119+
// having an additional interface in the interface list, instead of just having INHibernateProxy.
120+
// (Quite a loosy semantic...)
121+
new HashSet<System.Type> {typeof(INHibernateProxy), typeof(IPublic)},
122+
null, null, null);
123+
124+
#if NETFX
125+
VerifyGeneratedAssembly(
126+
() =>
127+
{
128+
#endif
129+
var proxy = factory.GetProxy(1, null);
130+
Assert.That(proxy, Is.Not.Null);
131+
Assert.That(proxy, Is.InstanceOf<IPublic>());
132+
Assert.That(proxy, Is.Not.InstanceOf<PublicInterfaceTestClass>());
133+
#if NETFX
134+
});
135+
#endif
136+
}
137+
110138
[Test]
111139
public void CanSerializeFieldInterceptorProxy()
112140
{

src/NHibernate/Proxy/NHibernateProxyBuilder.cs

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,16 @@ public NHibernateProxyBuilder(MethodInfo getIdentifierMethod, MethodInfo setIden
3636

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

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

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

94-
private void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
103+
private void CreateProxiedMethod(
104+
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
95105
{
96106
if (method == NHibernateProxyTypeLazyInitializerProperty.GetMethod)
97107
{
98108
ImplementGetLazyInitializer(typeBuilder, method, lazyInitializerField);
99109
}
100110
else if (method == _getIdentifierMethod)
101111
{
102-
ImplementGetIdentifier(typeBuilder, method, lazyInitializerField);
112+
ImplementGetIdentifier(typeBuilder, method, lazyInitializerField, parentType);
103113
}
104114
else if (method == _setIdentifierMethod)
105115
{
106-
ImplementSetIdentifier(typeBuilder, method, lazyInitializerField);
116+
ImplementSetIdentifier(typeBuilder, method, lazyInitializerField, parentType);
107117
}
108118
else if (!_overridesEquals && method.Name == "Equals" && method.GetBaseDefinition() == typeof(object).GetMethod("Equals", new[] {typeof(object)}))
109119
{
@@ -115,11 +125,11 @@ private void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, Fie
115125
}
116126
else if (_componentIdType != null && _componentIdType.IsMethodOf(method))
117127
{
118-
ImplementCallMethodOnEmbeddedComponentId(typeBuilder, method, lazyInitializerField);
128+
ImplementCallMethodOnEmbeddedComponentId(typeBuilder, method, lazyInitializerField, parentType);
119129
}
120130
else
121131
{
122-
ImplementCallMethodOnImplementation(typeBuilder, method, lazyInitializerField);
132+
ImplementCallMethodOnImplementation(typeBuilder, method, lazyInitializerField, parentType);
123133
}
124134
}
125135

@@ -217,7 +227,8 @@ private static void ImplementGetLazyInitializer(TypeBuilder typeBuilder, MethodI
217227
typeBuilder.DefineMethodOverride(getMethod, method);
218228
}
219229

220-
private static void ImplementGetIdentifier(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
230+
private static void ImplementGetIdentifier(
231+
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
221232
{
222233
/*
223234
get
@@ -231,7 +242,7 @@ private static void ImplementGetIdentifier(TypeBuilder typeBuilder, MethodInfo m
231242

232243
var IL = methodOverride.GetILGenerator();
233244

234-
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
245+
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);
235246

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

245-
private static void ImplementSetIdentifier(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
256+
private static void ImplementSetIdentifier(
257+
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
246258
{
247259
/*
248-
set
260+
set
249261
{
250262
if (this.__lazyInitializer == null)
251263
return base.set_<Identifier>(value);
@@ -258,7 +270,7 @@ private static void ImplementSetIdentifier(TypeBuilder typeBuilder, MethodInfo m
258270
var methodOverride = ProxyBuilderHelper.GenerateMethodSignature(method.Name, method, typeBuilder);
259271
var IL = methodOverride.GetILGenerator();
260272

261-
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
273+
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);
262274

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

281-
private static void ImplementCallMethodOnEmbeddedComponentId(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
293+
private static void ImplementCallMethodOnEmbeddedComponentId(
294+
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
282295
{
283296
/*
284297
if (this.__lazyInitializer == null)
@@ -289,7 +302,7 @@ private static void ImplementCallMethodOnEmbeddedComponentId(TypeBuilder typeBui
289302

290303
var IL = methodOverride.GetILGenerator();
291304

292-
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
305+
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);
293306

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

304-
private static void ImplementCallMethodOnImplementation(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField)
317+
private static void ImplementCallMethodOnImplementation(
318+
TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
305319
{
306320
/*
307321
if (this.__lazyInitializer == null)
308322
return base.<Method>(args..);
309-
return this.__lazyInitializer.GetImplementation().<Method>(args..)
323+
return this.__lazyInitializer.GetImplementation().<Method>(args..)
310324
*/
311325
var methodOverride = ProxyBuilderHelper.GenerateMethodSignature(method.Name, method, typeBuilder);
312326

313327
var IL = methodOverride.GetILGenerator();
314328

315-
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField);
329+
EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField, parentType);
316330

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

320334
typeBuilder.DefineMethodOverride(methodOverride, method);
321335
}
322336

323-
private static void EmitCallBaseIfLazyInitializerIsNull(ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField)
337+
private static void EmitCallBaseIfLazyInitializerIsNull(
338+
ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField, System.Type parentType)
324339
{
325-
//if (this.__lazyInitializer == null)
326-
// return base.<Method>(args..)
340+
/*
341+
<if (method.DeclaringType.IsAssignableFrom(parentType))
342+
{>
343+
if (this.__lazyInitializer == null)
344+
return base.<method>(args..)
345+
<}>
346+
*/
347+
if (!method.DeclaringType.IsAssignableFrom(parentType))
348+
// The proxy does not derive from a type implementing the method, do not attempt
349+
// calling its base. In such case, the lazy initializer is never null.
350+
return;
351+
352+
// When deriving from the entity class, the entity class constructor may trigger
353+
// virtual calls accessing the proxy state before its own constructor has a chance
354+
// to initialize it. So although lazyInitializer is never supplied as null to the
355+
// proxy constructor, we must guard nonetheless against it being null during base
356+
// constructor call.
327357

328358
IL.Emit(OpCodes.Ldarg_0);
329359
IL.Emit(OpCodes.Ldfld, lazyInitializerField);

0 commit comments

Comments
 (0)