Skip to content

Commit 1920d17

Browse files
LordJZhazzik
authored andcommitted
NH-3911 - Fixed InvalidCastException in ReflectionOptimizer
Property and field may have different incompatible types e.g. shadowed by a derived class to return a more specific type. Make sure we handle this correctly.
1 parent e8daecb commit 1920d17

File tree

5 files changed

+56
-4
lines changed

5 files changed

+56
-4
lines changed

src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using NHibernate.Bytecode;
23
using NHibernate.Bytecode.Lightweight;
34
using NHibernate.Properties;
45
using NUnit.Framework;
@@ -53,5 +54,41 @@ public void NoGetter()
5354

5455
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters));
5556
}
57+
58+
public class GetterTypeMismatchClass
59+
{
60+
object _property;
61+
62+
public string Property
63+
{
64+
get { return _property as string ?? "str"; }
65+
}
66+
}
67+
68+
// Property and field may have different incompatible types
69+
// e.g. shadowed by a derived class to return a more specific type.
70+
// Make sure we handle this correctly.
71+
[Test]
72+
public void TestGetterTypeMismatch()
73+
{
74+
var obj = new GetterTypeMismatchClass();
75+
const string property = "Property";
76+
77+
NoSetterAccessor accessor = new NoSetterAccessor(new CamelCaseUnderscoreStrategy());
78+
Assert.IsTrue(accessor.CanAccessThroughReflectionOptimizer);
79+
80+
ReflectionOptimizer reflectionOptimizer = new ReflectionOptimizer(
81+
obj.GetType(),
82+
new[] { accessor.GetGetter(obj.GetType(), property) },
83+
new[] { accessor.GetSetter(obj.GetType(), property) });
84+
85+
IAccessOptimizer accessOptimizer = reflectionOptimizer.AccessOptimizer;
86+
87+
accessOptimizer.SetPropertyValues(obj, new object[] { 10 });
88+
object[] values = accessOptimizer.GetPropertyValues(obj);
89+
90+
Assert.AreEqual("str", values[0]);
91+
}
92+
5693
}
5794
}

src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[]
4141
//this.setters = setters;
4242

4343
GetPropertyValuesInvoker getInvoker = GenerateGetPropertyValuesMethod(getters);
44-
SetPropertyValuesInvoker setInvoker = GenerateSetPropertyValuesMethod(getters, setters);
44+
SetPropertyValuesInvoker setInvoker = GenerateSetPropertyValuesMethod(setters);
4545

4646
accessOptimizer = new AccessOptimizer(getInvoker, setInvoker, getters, setters);
4747

@@ -186,7 +186,7 @@ private GetPropertyValuesInvoker GenerateGetPropertyValuesMethod(IGetter[] gette
186186
/// Generates a dynamic method on the given type.
187187
/// </summary>
188188
/// <returns></returns>
189-
private SetPropertyValuesInvoker GenerateSetPropertyValuesMethod(IGetter[] getters, ISetter[] setters)
189+
private SetPropertyValuesInvoker GenerateSetPropertyValuesMethod(ISetter[] setters)
190190
{
191191
var methodArguments = new[] {typeof (object), typeof (object[]), typeof (SetterCallback)};
192192
DynamicMethod method = CreateDynamicMethod(null, methodArguments);
@@ -203,7 +203,6 @@ private SetPropertyValuesInvoker GenerateSetPropertyValuesMethod(IGetter[] gette
203203
{
204204
// get the member accessor
205205
ISetter setter = setters[i];
206-
System.Type valueType = getters[i].ReturnType;
207206

208207
var optimizableSetter = setter as IOptimizableSetter;
209208

@@ -217,7 +216,7 @@ private SetPropertyValuesInvoker GenerateSetPropertyValuesMethod(IGetter[] gette
217216
il.Emit(OpCodes.Ldc_I4, i);
218217
il.Emit(OpCodes.Ldelem_Ref);
219218

220-
EmitUtil.PreparePropertyForSet(il, valueType);
219+
EmitUtil.PreparePropertyForSet(il, optimizableSetter.Type);
221220

222221
// using the setter's emitted IL
223222
optimizableSetter.Emit(il);

src/NHibernate/Properties/BasicPropertyAccessor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,11 @@ public MethodInfo Method
346346
get { return property.GetSetMethod(true); }
347347
}
348348

349+
public System.Type Type
350+
{
351+
get { return property.PropertyType; }
352+
}
353+
349354
#endregion
350355

351356
public void Emit(ILGenerator il)

src/NHibernate/Properties/FieldAccessor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ public MethodInfo Method
320320
get { return null; }
321321
}
322322

323+
public System.Type Type
324+
{
325+
get { return field.FieldType; }
326+
}
327+
323328
#endregion
324329

325330
public void Emit(ILGenerator il)

src/NHibernate/Properties/IOptimizableSetter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ namespace NHibernate.Properties
88
/// </summary>
99
public interface IOptimizableSetter
1010
{
11+
/// <summary>
12+
/// When implemented by a class, gets the <see cref="System.Type"/> of the Property/Field.
13+
/// </summary>
14+
/// <value>The <see cref="System.Type"/> of the Property/Field.</value>
15+
System.Type Type { get; }
16+
1117
/// <summary>
1218
/// Emit IL to set the property of an object to the value. The object
1319
/// is loaded onto the stack first, then the value, then this method

0 commit comments

Comments
 (0)