From 4edb0668dc7709ab5c3192882e74575e30cdba54 Mon Sep 17 00:00:00 2001 From: maca88 Date: Fri, 21 Dec 2018 18:26:51 +0100 Subject: [PATCH 1/8] Extend IAccessOptimizer to support getting/setting single property value --- .../NHSpecificTest/NH3119/FixtureByCode.cs | 2 +- .../AccessorPerformanceFixture.cs | 135 ++++++++++++++++++ ...BasicPropertyAccessorPerformanceFixture.cs | 37 +++++ .../FieldAccessorPerformanceFixture.cs | 40 ++++++ .../ReflectionOptimizerTest/LcgFixture.cs | 8 +- src/NHibernate/Bytecode/IAccessOptimizer.cs | 66 ++++++++- src/NHibernate/Bytecode/IBytecodeProvider.cs | 33 ++++- .../Bytecode/Lightweight/AccessOptimizer.cs | 85 +++++++++-- .../Lightweight/BytecodeProviderImpl.cs | 20 ++- src/NHibernate/Bytecode/Lightweight/Getter.cs | 31 ++++ .../Lightweight/ReflectionOptimizer.cs | 75 +++++++++- src/NHibernate/Bytecode/Lightweight/Setter.cs | 31 ++++ src/NHibernate/Properties/FieldAccessor.cs | 5 + .../Properties/IOptimizableSetter.cs | 20 ++- .../Tuple/Component/PocoComponentTuplizer.cs | 28 +++- .../Tuple/Entity/AbstractEntityTuplizer.cs | 37 +++-- .../Tuple/Entity/PocoEntityTuplizer.cs | 52 ++++++- 17 files changed, 675 insertions(+), 30 deletions(-) create mode 100644 src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs create mode 100644 src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs create mode 100644 src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs create mode 100644 src/NHibernate/Bytecode/Lightweight/Getter.cs create mode 100644 src/NHibernate/Bytecode/Lightweight/Setter.cs diff --git a/src/NHibernate.Test/NHSpecificTest/NH3119/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/NH3119/FixtureByCode.cs index a766c205945..7fc4db2389d 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3119/FixtureByCode.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3119/FixtureByCode.cs @@ -131,7 +131,7 @@ public class ComponentTestReflectionOptimizer : ReflectionOptimizer public static bool IsCalledForComponent { get; set; } public ComponentTestReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setters) : - base(mappedType, getters, setters) + base(mappedType, getters, setters, null, null) { _logCall = mappedType == typeof(Component); } diff --git a/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs b/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs new file mode 100644 index 00000000000..3e6b0277b3c --- /dev/null +++ b/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using NHibernate.Bytecode; +using NHibernate.Bytecode.Lightweight; +using NHibernate.Properties; +using NUnit.Framework; + +namespace NHibernate.Test.PropertyTest +{ + public abstract class AccessorPerformanceFixture where T : new() + { + private IPropertyAccessor _accessor; + private IAccessOptimizer _optimizer; + private ISetter[] _setters; + private IGetter[] _getters; + + protected abstract string AccessorType { get; } + + protected abstract List PropertyNames { get; } + + protected abstract object GetValue(int i); + + [SetUp] + public void SetUp() + { + _accessor = PropertyAccessorFactory.GetPropertyAccessor(AccessorType); + + _getters = new IGetter[PropertyNames.Count]; + _setters = new ISetter[PropertyNames.Count]; + var type = typeof(T); + + for (var i = 0; i < PropertyNames.Count; i++) + { + _getters[i] = _accessor.GetGetter(type, PropertyNames[i]); + _setters[i] = _accessor.GetSetter(type, PropertyNames[i]); + } + + _optimizer = new ReflectionOptimizer(type, _getters, _setters, null, null).AccessOptimizer; + } + + [TestCase(50000)] + [TestCase(100000)] + [TestCase(200000)] + [TestCase(500000)] + public void TestGetPropertyValue(int iter) + { + var target = new T(); + var stopwatch = new Stopwatch(); + + // Warm up + TestGetter(target, 100); + TestOptimizedGetter(target, 100); + + stopwatch.Restart(); + TestGetter(target, iter); + stopwatch.Stop(); + Console.WriteLine($"Reflection getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + + stopwatch.Restart(); + TestOptimizedGetter(target, iter); + stopwatch.Stop(); + Console.WriteLine($"IL getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + } + + [TestCase(50000)] + [TestCase(100000)] + [TestCase(200000)] + [TestCase(500000)] + public void TestSetPropertyValue(int iter) + { + var target = new T(); + var stopwatch = new Stopwatch(); + + // Warm up + TestSetter(target, 100); + TestOptimizedSetter(target, 100); + + stopwatch.Restart(); + TestSetter(target, iter); + stopwatch.Stop(); + Console.WriteLine($"Reflection setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + + stopwatch.Restart(); + TestOptimizedSetter(target, iter); + stopwatch.Stop(); + Console.WriteLine($"IL setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + } + + private void TestGetter(object target, int iter) + { + for (var i = 0; i < iter; i++) + { + for (var j = 0; j < _getters.Length; j++) + { + var val = _getters[j].Get(target); + } + } + } + + private void TestOptimizedGetter(object target, int iter) + { + for (var i = 0; i < iter; i++) + { + for (var j = 0; j < _getters.Length; j++) + { + var val = _optimizer.GetPropertyValue(target, j); + } + } + } + + private void TestSetter(object target, int iter) + { + for (var i = 0; i < iter; i++) + { + for (var j = 0; j < _setters.Length; j++) + { + _setters[j].Set(target, GetValue(j)); + } + } + } + + private void TestOptimizedSetter(object target, int iter) + { + for (var i = 0; i < iter; i++) + { + for (var j = 0; j < _setters.Length; j++) + { + _optimizer.SetPropertyValue(target, j, GetValue(j)); + } + } + } + } +} diff --git a/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs b/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs new file mode 100644 index 00000000000..39735691249 --- /dev/null +++ b/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using NUnit.Framework; + +namespace NHibernate.Test.PropertyTest +{ + [TestFixture, Explicit] + public class BasicPropertyAccessorPerformanceFixture : AccessorPerformanceFixture + { + protected override string AccessorType => "property"; + + protected override List PropertyNames => new List + { + nameof(A.Id), + nameof(A.Name) + }; + + protected override object GetValue(int i) + { + switch (i) + { + case 0: + return 5; + case 1: + return "name"; + } + + return null; + } + + public class A + { + public int Id { get; set; } + + public string Name { get; set; } + } + } +} diff --git a/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs b/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs new file mode 100644 index 00000000000..2bc4a34beb0 --- /dev/null +++ b/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using NUnit.Framework; + +namespace NHibernate.Test.PropertyTest +{ + [TestFixture, Explicit] + public class FieldAccessorPerformanceFixture : AccessorPerformanceFixture + { + protected override string AccessorType => "field"; + + protected override List PropertyNames => new List + { + "_id", + "_name" + }; + + protected override object GetValue(int i) + { + switch (i) + { + case 0: + return 5; + case 1: + return "name"; + } + + return null; + } + + public class A + { + private int _id = 5; + private string _name =string.Empty; + + public int Id => _id; + + public string Name => _name; + } + } +} diff --git a/src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs b/src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs index 6ea091c70f9..e248515cfc3 100644 --- a/src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs +++ b/src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs @@ -29,7 +29,7 @@ public void NoSetter() new BasicPropertyAccessor.BasicSetter(typeof (NoSetterClass), typeof (NoSetterClass).GetProperty("Property"), "Property") }; - Assert.Throws(() => new ReflectionOptimizer(typeof(NoSetterClass), getters, setters)); + Assert.Throws(() => new ReflectionOptimizer(typeof(NoSetterClass), getters, setters, null, null)); } public class NoGetterClass @@ -52,7 +52,7 @@ public void NoGetter() new BasicPropertyAccessor.BasicSetter(typeof (NoGetterClass), typeof (NoGetterClass).GetProperty("Property"), "Property") }; - Assert.Throws(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters)); + Assert.Throws(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters, null, null)); } public class GetterTypeMismatchClass @@ -83,7 +83,9 @@ public void TestGetterTypeMismatch() ReflectionOptimizer reflectionOptimizer = new ReflectionOptimizer( obj.GetType(), new[] { accessor.GetGetter(obj.GetType(), property) }, - new[] { accessor.GetSetter(obj.GetType(), property) }); + new[] { accessor.GetSetter(obj.GetType(), property) }, + null, + null); IAccessOptimizer accessOptimizer = reflectionOptimizer.AccessOptimizer; diff --git a/src/NHibernate/Bytecode/IAccessOptimizer.cs b/src/NHibernate/Bytecode/IAccessOptimizer.cs index a3ade00495d..7827fe6826a 100644 --- a/src/NHibernate/Bytecode/IAccessOptimizer.cs +++ b/src/NHibernate/Bytecode/IAccessOptimizer.cs @@ -1,3 +1,6 @@ +using System; +using NHibernate.Bytecode.Lightweight; + namespace NHibernate.Bytecode { /// @@ -8,4 +11,65 @@ public interface IAccessOptimizer object[] GetPropertyValues(object target); void SetPropertyValues(object target, object[] values); } -} \ No newline at end of file + + public static class AccessOptimizerExtensions + { + /// + /// Get the property value on the given index. + /// + //6.0 TODO: Merge into IAccessOptimizer. + public static object GetPropertyValue(this IAccessOptimizer optimizer, object target, int i) + { + if (optimizer is AccessOptimizer accessOptimizer) + { + return accessOptimizer.GetPropertyValue(target, i); + } + + throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(GetPropertyValue)} method."); + } + + /// + /// Set the property value on the given index. + /// + //6.0 TODO: Merge into IAccessOptimizer. + public static void SetPropertyValue(this IAccessOptimizer optimizer, object target, int i, object value) + { + if (optimizer is AccessOptimizer accessOptimizer) + { + accessOptimizer.SetPropertyValue(target, i, value); + return; + } + + throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(SetPropertyValue)} method."); + } + + /// + /// Get the specialized property value. + /// + //6.0 TODO: Merge into IAccessOptimizer. + internal static object GetSpecializedPropertyValue(this IAccessOptimizer optimizer, object target) + { + if (optimizer is AccessOptimizer accessOptimizer) + { + return accessOptimizer.GetSpecializedPropertyValue(target); + } + + throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(GetPropertyValue)} method."); + } + + /// + /// Set the specialized property value. + /// + //6.0 TODO: Merge into IAccessOptimizer. + internal static void SetSpecializedPropertyValue(this IAccessOptimizer optimizer, object target, object value) + { + if (optimizer is AccessOptimizer accessOptimizer) + { + accessOptimizer.SetSpecializedPropertyValue(target, value); + return; + } + + throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(SetPropertyValue)} method."); + } + } +} diff --git a/src/NHibernate/Bytecode/IBytecodeProvider.cs b/src/NHibernate/Bytecode/IBytecodeProvider.cs index 2b9b4c29aa5..6f75c4830a0 100644 --- a/src/NHibernate/Bytecode/IBytecodeProvider.cs +++ b/src/NHibernate/Bytecode/IBytecodeProvider.cs @@ -1,4 +1,5 @@ using System; +using NHibernate.Bytecode.Lightweight; using NHibernate.Properties; namespace NHibernate.Bytecode @@ -19,6 +20,8 @@ public interface IBytecodeProvider /// All property getters to be accessed via reflection. /// All property setters to be accessed via reflection. /// The reflection optimization delegate. + // Since 5.3 + [Obsolete("Please use NHibernate.Bytecode.BytecodeProviderExtensions.GetReflectionOptimizer instead")] IReflectionOptimizer GetReflectionOptimizer(System.Type clazz, IGetter[] getters, ISetter[] setters); /// @@ -48,4 +51,32 @@ public interface IBytecodeProvider // Not ported //ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter); } -} \ No newline at end of file + + public static class BytecodeProviderExtensions + { + /// + /// Retrieve the delegate for this provider + /// capable of generating reflection optimization components. + /// + /// The bytecode provider. + /// The class to be reflected upon. + /// All property getters to be accessed via reflection. + /// All property setters to be accessed via reflection. + /// The specialized getter for the given type. + /// The specialized setter for the given type. + /// The reflection optimization delegate. + //6.0 TODO: Merge into IBytecodeProvider. + public static IReflectionOptimizer GetReflectionOptimizer(this IBytecodeProvider bytecodeProvider, System.Type clazz, IGetter[] getters, + ISetter[] setters, IGetter specializedGetter, ISetter specializedSetter) + { + if (bytecodeProvider is BytecodeProviderImpl bytecodeProviderImpl) + { + return bytecodeProviderImpl.GetReflectionOptimizer(clazz, getters, setters, specializedGetter, specializedSetter); + } + +#pragma warning disable 618 + return bytecodeProvider.GetReflectionOptimizer(clazz, getters, setters); +#pragma warning restore 618 + } + } +} diff --git a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs index 0d5b666a436..27138b65c46 100644 --- a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs @@ -1,3 +1,6 @@ +using System; +using System.Linq; +using System.Security; using NHibernate.Properties; namespace NHibernate.Bytecode.Lightweight @@ -6,20 +9,42 @@ public class AccessOptimizer : IAccessOptimizer { private readonly GetPropertyValuesInvoker getDelegate; private readonly SetPropertyValuesInvoker setDelegate; - private readonly IGetter[] getters; - private readonly ISetter[] setters; private readonly GetterCallback getterCallback; private readonly SetterCallback setterCallback; + private readonly Getter[] _getters; + private readonly Setter[] _setters; + private readonly Getter _specializedGetter; + private readonly Setter _specializedSetter; + // Since 5.3 + [Obsolete("This constructor has no usages and will be removed in a future version")] public AccessOptimizer(GetPropertyValuesInvoker getDelegate, SetPropertyValuesInvoker setDelegate, IGetter[] getters, ISetter[] setters) + : this( + getDelegate, + setDelegate, + getters.Select(o => new Getter(o)).ToArray(), + setters.Select(o => new Setter(o)).ToArray(), + null, + null) + { + } + + public AccessOptimizer(GetPropertyValuesInvoker getDelegate, + SetPropertyValuesInvoker setDelegate, + Getter[] getters, + Setter[] setters, + Getter specializedGetter, + Setter specializedSetter) { this.getDelegate = getDelegate; this.setDelegate = setDelegate; - this.getters = getters; - this.setters = setters; - getterCallback = OnGetterCallback; - setterCallback = OnSetterCallback; + _getters = getters; + _setters = setters; + _specializedGetter = specializedGetter; + _specializedSetter = specializedSetter; + getterCallback = GetPropertyValue; + setterCallback = SetPropertyValue; } public object[] GetPropertyValues(object target) @@ -32,14 +57,54 @@ public void SetPropertyValues(object target, object[] values) setDelegate(target, values, setterCallback); } - private object OnGetterCallback(object target, int i) + public void SetPropertyValue(object target, int i, object value) + { + SetPropertyValue(target, value, _setters[i]); + } + + public object GetPropertyValue(object target, int i) + { + return GetPropertyValue(target, _getters[i]); + } + + internal void SetSpecializedPropertyValue(object target, object value) + { + SetPropertyValue(target, value, _specializedSetter); + } + + internal object GetSpecializedPropertyValue(object target) { - return getters[i].Get(target); + return GetPropertyValue(target, _specializedGetter); + } + + private static object GetPropertyValue(object target, Getter getter) + { + if (getter.Optimized == null) + { + return getter.Default.Get(target); + } + + return getter.Optimized(target); } - private void OnSetterCallback(object target, int i, object value) + private static void SetPropertyValue(object target, object value, Setter setter) { - setters[i].Set(target, value); + if (setter.Optimized == null) + { + setter.Default.Set(target, value); + } + else + { + // 6.0 TODO: remove the try/catch block once CanEmit will be part of the IOptimizableSetter + try + { + setter.Optimized(target, value); + } + catch (VerificationException) // Will occur for readonly fields and will heavily impact on performance when occurred. + { + setter.Default.Set(target, value); + } + } } } } diff --git a/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs b/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs index d1f7b63b9da..280f5730be4 100644 --- a/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs +++ b/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs @@ -24,9 +24,25 @@ public class BytecodeProviderImpl : AbstractBytecodeProvider /// if the generation fails public override IReflectionOptimizer GetReflectionOptimizer(System.Type mappedClass, IGetter[] getters, ISetter[] setters) { - return new ReflectionOptimizer(mappedClass, getters, setters); + return new ReflectionOptimizer(mappedClass, getters, setters, null, null); } #endregion + + /// + /// Retrieve the delegate for this provider + /// capable of generating reflection optimization components. + /// + /// The class to be reflected upon. + /// All property getters to be accessed via reflection. + /// All property setters to be accessed via reflection. + /// The specialized getter for the given type. + /// The specialized setter for the given type. + /// The reflection optimization delegate. + internal IReflectionOptimizer GetReflectionOptimizer(System.Type mappedClass, IGetter[] getters, ISetter[] setters, + IGetter specializedGetter, ISetter specializedSetter) + { + return new ReflectionOptimizer(mappedClass, getters, setters, specializedGetter, specializedSetter); + } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Bytecode/Lightweight/Getter.cs b/src/NHibernate/Bytecode/Lightweight/Getter.cs new file mode 100644 index 00000000000..50a2bbf4ed8 --- /dev/null +++ b/src/NHibernate/Bytecode/Lightweight/Getter.cs @@ -0,0 +1,31 @@ +using System; +using NHibernate.Properties; + +namespace NHibernate.Bytecode.Lightweight +{ + /// + /// Contains the instance with an optional optimized delegate. + /// + public class Getter + { + public Getter(IGetter @default) : this(@default, null) + { + } + + public Getter(IGetter @default, Func optimized) + { + Default = @default; + Optimized = optimized; + } + + /// + /// The default instance. + /// + public IGetter Default { get; } + + /// + /// Optimized getter delegate. + /// + public Func Optimized { get; } + } +} diff --git a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs index 176a69be8a9..c6fa2727eb7 100644 --- a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Security; @@ -34,7 +35,17 @@ public virtual object CreateInstance() /// /// Class constructor. /// + [Obsolete("This constructor has no usages and will be removed in a future version")] public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setters) + : this(mappedType, getters, setters, null, null) + { + } + + /// + /// Class constructor. + /// + public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setters, + IGetter specializedGetter, ISetter specializedSetter) { // save off references this.mappedType = mappedType; @@ -45,7 +56,29 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] GetPropertyValuesInvoker getInvoker = GenerateGetPropertyValuesMethod(getters); SetPropertyValuesInvoker setInvoker = GenerateSetPropertyValuesMethod(setters); - accessOptimizer = new AccessOptimizer(getInvoker, setInvoker, getters, setters); + var getMethods = new Getter[getters.Length]; + for (var i = 0; i < getters.Length; i++) + { + getMethods[i] = new Getter(getters[i], GenerateGetPropertyValueMethod(getters[i])); + } + + var setMethods = new Setter[setters.Length]; + for (var i = 0; i < setters.Length; i++) + { + setMethods[i] = new Setter(setters[i], GenerateSetPropertyValueMethod(setters[i])); + } + + accessOptimizer = new AccessOptimizer( + getInvoker, + setInvoker, + getMethods, + setMethods, + new Getter( + specializedGetter, + specializedGetter != null ? GenerateGetPropertyValueMethod(specializedGetter) : null), + new Setter( + specializedSetter, + specializedSetter != null ? GenerateSetPropertyValueMethod(specializedSetter) : null)); createInstanceMethod = CreateCreateInstanceMethod(mappedType); } @@ -132,6 +165,46 @@ private static void EmitCastToReference(ILGenerator il, System.Type type) private static readonly MethodInfo GetterCallbackInvoke = ReflectHelper.GetMethod( g => g.Invoke(null, 0)); + private Func GenerateGetPropertyValueMethod(IGetter getter) + { + if (!(getter is IOptimizableGetter optimizableGetter)) + { + return null; + } + + var method = new DynamicMethod(string.Empty, typeof(object), new[] { typeof(object) }, CanSkipVisibilityChecks()); + var il = method.GetILGenerator(); + + // object (target) { (object) ((ClassType) target).GetMethod(); } + il.Emit(OpCodes.Ldarg_0); + EmitCastToReference(il, mappedType); + optimizableGetter.Emit(il); + EmitUtil.EmitBoxIfNeeded(il, getter.ReturnType); + il.Emit(OpCodes.Ret); + + return (Func) method.CreateDelegate(typeof(Func)); + } + + private Action GenerateSetPropertyValueMethod(ISetter setter) + { + if (!(setter is IOptimizableSetter optimizableSetter) || !optimizableSetter.CanEmit()) + { + return null; + } + + // void (target, value) { ((ClassType) target).SetMethod((FieldType) value); } + var method = new DynamicMethod(string.Empty, null, new[] { typeof(object), typeof(object) }, CanSkipVisibilityChecks()); + var il = method.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + EmitCastToReference(il, mappedType); + il.Emit(OpCodes.Ldarg_1); + il.Emit(optimizableSetter.Type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, optimizableSetter.Type); + optimizableSetter.Emit(il); + il.Emit(OpCodes.Ret); + + return (Action) method.CreateDelegate(typeof(Action)); + } + /// /// Generates a dynamic method on the given type. /// diff --git a/src/NHibernate/Bytecode/Lightweight/Setter.cs b/src/NHibernate/Bytecode/Lightweight/Setter.cs new file mode 100644 index 00000000000..cd45686e0d6 --- /dev/null +++ b/src/NHibernate/Bytecode/Lightweight/Setter.cs @@ -0,0 +1,31 @@ +using System; +using NHibernate.Properties; + +namespace NHibernate.Bytecode.Lightweight +{ + /// + /// Contains the instance with an optional optimized delegate. + /// + public class Setter + { + public Setter(ISetter @default) : this(@default, null) + { + } + + public Setter(ISetter @default, Action optimized) + { + Default = @default; + Optimized = optimized; + } + + /// + /// The default instance. + /// + public ISetter Default { get; } + + /// + /// Optimized setter delegate. + /// + public Action Optimized { get; } + } +} diff --git a/src/NHibernate/Properties/FieldAccessor.cs b/src/NHibernate/Properties/FieldAccessor.cs index 5a4b4df3854..8d78d2a79ed 100644 --- a/src/NHibernate/Properties/FieldAccessor.cs +++ b/src/NHibernate/Properties/FieldAccessor.cs @@ -331,6 +331,11 @@ public void Emit(ILGenerator il) { il.Emit(OpCodes.Stfld, field); } + + internal bool CanEmit() + { + return !field.IsInitOnly; + } } } } diff --git a/src/NHibernate/Properties/IOptimizableSetter.cs b/src/NHibernate/Properties/IOptimizableSetter.cs index e45eb7c8ab0..ece70b56e88 100644 --- a/src/NHibernate/Properties/IOptimizableSetter.cs +++ b/src/NHibernate/Properties/IOptimizableSetter.cs @@ -21,4 +21,22 @@ public interface IOptimizableSetter /// void Emit(ILGenerator il); } -} \ No newline at end of file + + internal static class OptimizableSetterExtensions + { + /// + /// Determine if the optimizable setter can actually be optimized. + /// + /// if can be optimized, otherwise. + //6.0 TODO: Merge into IOptimizableSetter. + public static bool CanEmit(this IOptimizableSetter setter) + { + if (setter is FieldAccessor.FieldSetter fieldSetter) + { + return fieldSetter.CanEmit(); + } + + return true; + } + } +} diff --git a/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs b/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs index e94b6afc265..d7fb4824212 100644 --- a/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs +++ b/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs @@ -1,5 +1,6 @@ using System; using NHibernate.Bytecode; +using NHibernate.Bytecode.Lightweight; using NHibernate.Intercept; using NHibernate.Properties; @@ -18,6 +19,7 @@ public class PocoComponentTuplizer : AbstractComponentTuplizer private readonly IGetter parentGetter; [NonSerialized] private IReflectionOptimizer optimizer; + private readonly bool isBytecodeProviderImpl; // 6.0 TODO: remove [OnDeserialized] @@ -52,6 +54,7 @@ public PocoComponentTuplizer(Mapping.Component component) parentGetter = parentProperty.GetGetter(componentClass); } + isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl; SetReflectionOptimizer(); // Fix for NH-3119 @@ -95,13 +98,36 @@ public override void SetPropertyValues(object component, object[] values) } } + public override object GetPropertyValue(object component, int i) + { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + return component == null + ? null + : optimizer.AccessOptimizer.GetPropertyValue(component, i); + } + + return base.GetPropertyValue(component, i); + } + public override object GetParent(object component) { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + return optimizer.AccessOptimizer.GetSpecializedPropertyValue(component); + } + return parentGetter.Get(component); } public override void SetParent(object component, object parent, Engine.ISessionFactoryImplementor factory) { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + optimizer.AccessOptimizer.SetSpecializedPropertyValue(component, parent); + return; + } + parentSetter.Set(component, parent); } @@ -142,7 +168,7 @@ protected void SetReflectionOptimizer() { if (Cfg.Environment.UseReflectionOptimizer) { - optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters); + optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters, parentGetter, parentSetter); } } diff --git a/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs index f4f6c4c0b60..4237e9b18f0 100644 --- a/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs +++ b/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; @@ -16,8 +17,8 @@ public abstract class AbstractEntityTuplizer : IEntityTuplizer { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(AbstractEntityTuplizer)); private readonly EntityMetamodel entityMetamodel; - private readonly IGetter idGetter; - private readonly ISetter idSetter; + protected readonly IGetter idGetter; + protected readonly ISetter idSetter; protected int propertySpan; protected IGetter[] getters; @@ -136,7 +137,7 @@ public object GetIdentifier(object entity) } else { - id = idGetter.Get(entity); + id = GetIdentifierPropertyValue(entity); } } @@ -155,7 +156,7 @@ public void SetIdentifier(object entity, object id) } else if (idSetter != null) { - idSetter.Set(entity, id); + SetIdentifierPropertyValue(entity, id); } } @@ -179,17 +180,27 @@ public object GetVersion(object entity) { if (!entityMetamodel.IsVersioned) return null; - return getters[entityMetamodel.VersionPropertyIndex].Get(entity); + return GetPropertyValue(entity, entityMetamodel.VersionPropertyIndex); } + // 6.0 TODO: make it virtual public void SetPropertyValue(object entity, int i, object value) { +#pragma warning disable 618 + SetValue(entity, i, value); +#pragma warning restore 618 + } + + // Since 5.3 + [Obsolete("Use SetPropertyValue instead.")] + protected virtual void SetValue(object entity, int i, object value) + { setters[i].Set(entity, value); } public void SetPropertyValue(object entity, string propertyName, object value) { - setters[entityMetamodel.GetPropertyIndex(propertyName)].Set(entity, value); + SetPropertyValue(entity, entityMetamodel.GetPropertyIndex(propertyName), value); } public virtual object[] GetPropertyValuesToInsert(object entity, IDictionary mergeMap, ISessionImplementor session) @@ -259,7 +270,7 @@ public virtual object[] GetPropertyValues(object entity) StandardProperty property = entityMetamodel.Properties[j]; if (getAll || !property.IsLazy) { - result[j] = getters[j].Get(entity); + result[j] = GetPropertyValue(entity, j); } else { @@ -277,7 +288,7 @@ public virtual void SetPropertyValues(object entity, object[] values) { if (setAll || !Equals(LazyPropertyInitializer.UnfetchedProperty, values[j])) { - setters[j].Set(entity, values[j]); + SetPropertyValue(entity, j, values[j]); } } } @@ -299,6 +310,16 @@ public bool IsInstance(object obj) #endregion + protected virtual object GetIdentifierPropertyValue(object entity) + { + return idGetter.Get(entity); + } + + protected virtual void SetIdentifierPropertyValue(object entity, object value) + { + idSetter.Set(entity, value); + } + /// Return the entity-mode handled by this tuplizer instance. public abstract EntityMode EntityMode { get;} diff --git a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs index 9f57cba92cf..ccd4966e92c 100644 --- a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs +++ b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs @@ -12,6 +12,7 @@ using NHibernate.Type; using NHibernate.Util; using System.Runtime.Serialization; +using NHibernate.Bytecode.Lightweight; namespace NHibernate.Tuple.Entity { @@ -29,6 +30,7 @@ public class PocoEntityTuplizer : AbstractEntityTuplizer [NonSerialized] private IReflectionOptimizer optimizer; private readonly IProxyValidator proxyValidator; + private readonly bool isBytecodeProviderImpl; // 6.0 TODO: remove [OnDeserialized] internal void OnDeserialized(StreamingContext context) @@ -48,7 +50,7 @@ protected void SetReflectionOptimizer() if (Cfg.Environment.UseReflectionOptimizer) { // NH different behavior fo NH-1587 - optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(mappedClass, getters, setters); + optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(mappedClass, getters, setters, idGetter, idSetter); } } public PocoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) @@ -66,6 +68,8 @@ public PocoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappe if (property.UnwrapProxy) unwrapProxyPropertyNames.Add(property.Name); } + + isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl; SetReflectionOptimizer(); Instantiator = BuildInstantiator(mappedEntity); @@ -238,6 +242,16 @@ public override void AfterInitialize(object entity, bool lazyPropertiesAreUnfetc } } + public override object GetPropertyValue(object entity, int i) + { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + return optimizer.AccessOptimizer.GetPropertyValue(entity, i); + } + + return base.GetPropertyValue(entity, i); + } + public override object[] GetPropertyValues(object entity) { if (ShouldGetAllProperties(entity) && optimizer != null && optimizer.AccessOptimizer != null) @@ -285,6 +299,21 @@ public override bool IsLifecycleImplementor get { return islifecycleImplementor; } } +#pragma warning disable 672 + protected override void SetValue(object entity, int i, object value) +#pragma warning restore 672 + { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + optimizer.AccessOptimizer.SetPropertyValue(entity, i, value); + return; + } + +#pragma warning disable 618 + base.SetValue(entity, i, value); +#pragma warning restore 618 + } + public override void SetPropertyValues(object entity, object[] values) { if (!EntityMetamodel.HasLazyProperties && optimizer != null && optimizer.AccessOptimizer != null) @@ -327,5 +356,26 @@ protected void ClearOptimizerWhenUsingCustomAccessors() optimizer = null; } } + + protected override object GetIdentifierPropertyValue(object entity) + { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + return optimizer.AccessOptimizer.GetSpecializedPropertyValue(entity); + } + + return base.GetIdentifierPropertyValue(entity); + } + + protected override void SetIdentifierPropertyValue(object entity, object value) + { + if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) + { + optimizer.AccessOptimizer.SetSpecializedPropertyValue(entity, value); + return; + } + + base.SetIdentifierPropertyValue(entity, value); + } } } From dca47d2415690fd9f195188ec5a077a96327435f Mon Sep 17 00:00:00 2001 From: maca88 Date: Sun, 23 Dec 2018 13:32:38 +0100 Subject: [PATCH 2/8] Fixed the IL generation of the get/set methods --- .../AccessorPerformanceFixture.cs | 48 +++++++++++++++---- ...BasicPropertyAccessorPerformanceFixture.cs | 23 +++++---- .../FieldAccessorPerformanceFixture.cs | 25 +++++----- src/NHibernate/Bytecode/IBytecodeProvider.cs | 5 +- .../Bytecode/Lightweight/AccessOptimizer.cs | 10 +--- .../Lightweight/ReflectionOptimizer.cs | 17 +++---- src/NHibernate/Properties/FieldAccessor.cs | 5 -- .../Properties/IOptimizableSetter.cs | 20 +------- .../Tuple/Component/PocoComponentTuplizer.cs | 4 +- .../Tuple/Entity/AbstractEntityTuplizer.cs | 12 +---- .../Tuple/Entity/PocoEntityTuplizer.cs | 13 ++--- 11 files changed, 82 insertions(+), 100 deletions(-) diff --git a/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs b/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs index 3e6b0277b3c..0653497e073 100644 --- a/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs +++ b/src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs @@ -20,7 +20,7 @@ namespace NHibernate.Test.PropertyTest protected abstract List PropertyNames { get; } - protected abstract object GetValue(int i); + protected abstract object[] GetValues(); [SetUp] public void SetUp() @@ -44,7 +44,7 @@ public void SetUp() [TestCase(100000)] [TestCase(200000)] [TestCase(500000)] - public void TestGetPropertyValue(int iter) + public void TestGetter(int iter) { var target = new T(); var stopwatch = new Stopwatch(); @@ -52,23 +52,29 @@ public void TestGetPropertyValue(int iter) // Warm up TestGetter(target, 100); TestOptimizedGetter(target, 100); + TestOptimizedMultiGetter(target, 100); stopwatch.Restart(); TestGetter(target, iter); stopwatch.Stop(); - Console.WriteLine($"Reflection getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + Console.WriteLine($"IGetter.Get total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); stopwatch.Restart(); TestOptimizedGetter(target, iter); stopwatch.Stop(); - Console.WriteLine($"IL getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + Console.WriteLine($"IAccessOptimizer.GetPropertyValue total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + + stopwatch.Restart(); + TestOptimizedMultiGetter(target, iter); + stopwatch.Stop(); + Console.WriteLine($"IAccessOptimizer.GetPropertyValues total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); } [TestCase(50000)] [TestCase(100000)] [TestCase(200000)] [TestCase(500000)] - public void TestSetPropertyValue(int iter) + public void TestSetter(int iter) { var target = new T(); var stopwatch = new Stopwatch(); @@ -76,16 +82,22 @@ public void TestSetPropertyValue(int iter) // Warm up TestSetter(target, 100); TestOptimizedSetter(target, 100); + TestOptimizedMultiSetter(target, 100); stopwatch.Restart(); TestSetter(target, iter); stopwatch.Stop(); - Console.WriteLine($"Reflection setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + Console.WriteLine($"ISetter.Set total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); stopwatch.Restart(); TestOptimizedSetter(target, iter); stopwatch.Stop(); - Console.WriteLine($"IL setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + Console.WriteLine($"IAccessOptimizer.SetPropertyValue total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); + + stopwatch.Restart(); + TestOptimizedMultiSetter(target, iter); + stopwatch.Stop(); + Console.WriteLine($"IAccessOptimizer.SetPropertyValues total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms"); } private void TestGetter(object target, int iter) @@ -110,13 +122,22 @@ private void TestOptimizedGetter(object target, int iter) } } + private void TestOptimizedMultiGetter(object target, int iter) + { + for (var i = 0; i < iter; i++) + { + var val = _optimizer.GetPropertyValues(target); + } + } + private void TestSetter(object target, int iter) { for (var i = 0; i < iter; i++) { + var values = GetValues(); for (var j = 0; j < _setters.Length; j++) { - _setters[j].Set(target, GetValue(j)); + _setters[j].Set(target, values[j]); } } } @@ -125,11 +146,20 @@ private void TestOptimizedSetter(object target, int iter) { for (var i = 0; i < iter; i++) { + var values = GetValues(); for (var j = 0; j < _setters.Length; j++) { - _optimizer.SetPropertyValue(target, j, GetValue(j)); + _optimizer.SetPropertyValue(target, j, values[j]); } } } + + private void TestOptimizedMultiSetter(object target, int iter) + { + for (var i = 0; i < iter; i++) + { + _optimizer.SetPropertyValues(target, GetValues()); + } + } } } diff --git a/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs b/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs index 39735691249..172c0891b16 100644 --- a/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs +++ b/src/NHibernate.Test/PropertyTest/BasicPropertyAccessorPerformanceFixture.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using NUnit.Framework; namespace NHibernate.Test.PropertyTest @@ -11,20 +12,14 @@ public class BasicPropertyAccessorPerformanceFixture : AccessorPerformanceFixtur protected override List PropertyNames => new List { nameof(A.Id), - nameof(A.Name) + nameof(A.Name), + nameof(A.Date), + nameof(A.Decimal) }; - protected override object GetValue(int i) + protected override object[] GetValues() { - switch (i) - { - case 0: - return 5; - case 1: - return "name"; - } - - return null; + return new object[] { 5, "name", DateTime.MaxValue, 1.5m }; } public class A @@ -32,6 +27,10 @@ public class A public int Id { get; set; } public string Name { get; set; } + + public DateTime Date { get; set; } + + public decimal? Decimal { get; set; } } } } diff --git a/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs b/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs index 2bc4a34beb0..09f3785c2e4 100644 --- a/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs +++ b/src/NHibernate.Test/PropertyTest/FieldAccessorPerformanceFixture.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using NUnit.Framework; namespace NHibernate.Test.PropertyTest @@ -11,30 +12,30 @@ public class FieldAccessorPerformanceFixture : AccessorPerformanceFixture PropertyNames => new List { "_id", - "_name" + "_name", + "_date", + "_decimal", }; - protected override object GetValue(int i) + protected override object[] GetValues() { - switch (i) - { - case 0: - return 5; - case 1: - return "name"; - } - - return null; + return new object[] {5, "name", DateTime.MaxValue, 1.5m}; } public class A { private int _id = 5; private string _name =string.Empty; + private DateTime _date = DateTime.MinValue; + private decimal? _decimal = decimal.Zero; public int Id => _id; public string Name => _name; + + public DateTime Date => _date; + + public decimal? Decimal => _decimal; } } } diff --git a/src/NHibernate/Bytecode/IBytecodeProvider.cs b/src/NHibernate/Bytecode/IBytecodeProvider.cs index 6f75c4830a0..d17daaa32b3 100644 --- a/src/NHibernate/Bytecode/IBytecodeProvider.cs +++ b/src/NHibernate/Bytecode/IBytecodeProvider.cs @@ -66,8 +66,9 @@ public static class BytecodeProviderExtensions /// The specialized setter for the given type. /// The reflection optimization delegate. //6.0 TODO: Merge into IBytecodeProvider. - public static IReflectionOptimizer GetReflectionOptimizer(this IBytecodeProvider bytecodeProvider, System.Type clazz, IGetter[] getters, - ISetter[] setters, IGetter specializedGetter, ISetter specializedSetter) + public static IReflectionOptimizer GetReflectionOptimizer( + this IBytecodeProvider bytecodeProvider, System.Type clazz, IGetter[] getters, ISetter[] setters, + IGetter specializedGetter, ISetter specializedSetter) { if (bytecodeProvider is BytecodeProviderImpl bytecodeProviderImpl) { diff --git a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs index 27138b65c46..651d95752eb 100644 --- a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs @@ -95,15 +95,7 @@ private static void SetPropertyValue(object target, object value, Setter setter) } else { - // 6.0 TODO: remove the try/catch block once CanEmit will be part of the IOptimizableSetter - try - { - setter.Optimized(target, value); - } - catch (VerificationException) // Will occur for readonly fields and will heavily impact on performance when occurred. - { - setter.Default.Set(target, value); - } + setter.Optimized(target, value); } } } diff --git a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs index c6fa2727eb7..6aa105d6671 100644 --- a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs @@ -73,12 +73,9 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setInvoker, getMethods, setMethods, - new Getter( - specializedGetter, - specializedGetter != null ? GenerateGetPropertyValueMethod(specializedGetter) : null), - new Setter( - specializedSetter, - specializedSetter != null ? GenerateSetPropertyValueMethod(specializedSetter) : null)); + new Getter(specializedGetter, GenerateGetPropertyValueMethod(specializedGetter)), + new Setter(specializedSetter, GenerateSetPropertyValueMethod(specializedSetter)) + ); createInstanceMethod = CreateCreateInstanceMethod(mappedType); } @@ -172,7 +169,7 @@ private Func GenerateGetPropertyValueMethod(IGetter getter) return null; } - var method = new DynamicMethod(string.Empty, typeof(object), new[] { typeof(object) }, CanSkipVisibilityChecks()); + var method = CreateDynamicMethod(typeof(object), new[] { typeof(object) }); var il = method.GetILGenerator(); // object (target) { (object) ((ClassType) target).GetMethod(); } @@ -187,18 +184,18 @@ private Func GenerateGetPropertyValueMethod(IGetter getter) private Action GenerateSetPropertyValueMethod(ISetter setter) { - if (!(setter is IOptimizableSetter optimizableSetter) || !optimizableSetter.CanEmit()) + if (!(setter is IOptimizableSetter optimizableSetter)) { return null; } // void (target, value) { ((ClassType) target).SetMethod((FieldType) value); } - var method = new DynamicMethod(string.Empty, null, new[] { typeof(object), typeof(object) }, CanSkipVisibilityChecks()); + var method = CreateDynamicMethod(null, new[] { typeof(object), typeof(object) }); var il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); EmitCastToReference(il, mappedType); il.Emit(OpCodes.Ldarg_1); - il.Emit(optimizableSetter.Type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, optimizableSetter.Type); + EmitUtil.PreparePropertyForSet(il, optimizableSetter.Type); optimizableSetter.Emit(il); il.Emit(OpCodes.Ret); diff --git a/src/NHibernate/Properties/FieldAccessor.cs b/src/NHibernate/Properties/FieldAccessor.cs index 8d78d2a79ed..5a4b4df3854 100644 --- a/src/NHibernate/Properties/FieldAccessor.cs +++ b/src/NHibernate/Properties/FieldAccessor.cs @@ -331,11 +331,6 @@ public void Emit(ILGenerator il) { il.Emit(OpCodes.Stfld, field); } - - internal bool CanEmit() - { - return !field.IsInitOnly; - } } } } diff --git a/src/NHibernate/Properties/IOptimizableSetter.cs b/src/NHibernate/Properties/IOptimizableSetter.cs index ece70b56e88..e45eb7c8ab0 100644 --- a/src/NHibernate/Properties/IOptimizableSetter.cs +++ b/src/NHibernate/Properties/IOptimizableSetter.cs @@ -21,22 +21,4 @@ public interface IOptimizableSetter /// void Emit(ILGenerator il); } - - internal static class OptimizableSetterExtensions - { - /// - /// Determine if the optimizable setter can actually be optimized. - /// - /// if can be optimized, otherwise. - //6.0 TODO: Merge into IOptimizableSetter. - public static bool CanEmit(this IOptimizableSetter setter) - { - if (setter is FieldAccessor.FieldSetter fieldSetter) - { - return fieldSetter.CanEmit(); - } - - return true; - } - } -} +} \ No newline at end of file diff --git a/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs b/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs index d7fb4824212..e45037e68c8 100644 --- a/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs +++ b/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs @@ -19,7 +19,7 @@ public class PocoComponentTuplizer : AbstractComponentTuplizer private readonly IGetter parentGetter; [NonSerialized] private IReflectionOptimizer optimizer; - private readonly bool isBytecodeProviderImpl; // 6.0 TODO: remove + private bool isBytecodeProviderImpl; // 6.0 TODO: remove [OnDeserialized] @@ -54,7 +54,6 @@ public PocoComponentTuplizer(Mapping.Component component) parentGetter = parentProperty.GetGetter(componentClass); } - isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl; SetReflectionOptimizer(); // Fix for NH-3119 @@ -169,6 +168,7 @@ protected void SetReflectionOptimizer() if (Cfg.Environment.UseReflectionOptimizer) { optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters, parentGetter, parentSetter); + isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl; } } diff --git a/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs index 4237e9b18f0..f9009c3c0d6 100644 --- a/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs +++ b/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs @@ -183,17 +183,7 @@ public object GetVersion(object entity) return GetPropertyValue(entity, entityMetamodel.VersionPropertyIndex); } - // 6.0 TODO: make it virtual - public void SetPropertyValue(object entity, int i, object value) - { -#pragma warning disable 618 - SetValue(entity, i, value); -#pragma warning restore 618 - } - - // Since 5.3 - [Obsolete("Use SetPropertyValue instead.")] - protected virtual void SetValue(object entity, int i, object value) + public virtual void SetPropertyValue(object entity, int i, object value) { setters[i].Set(entity, value); } diff --git a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs index ccd4966e92c..ae310903873 100644 --- a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs +++ b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs @@ -30,7 +30,7 @@ public class PocoEntityTuplizer : AbstractEntityTuplizer [NonSerialized] private IReflectionOptimizer optimizer; private readonly IProxyValidator proxyValidator; - private readonly bool isBytecodeProviderImpl; // 6.0 TODO: remove + private bool isBytecodeProviderImpl; // 6.0 TODO: remove [OnDeserialized] internal void OnDeserialized(StreamingContext context) @@ -51,6 +51,7 @@ protected void SetReflectionOptimizer() { // NH different behavior fo NH-1587 optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(mappedClass, getters, setters, idGetter, idSetter); + isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl; } } public PocoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) @@ -68,8 +69,6 @@ public PocoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappe if (property.UnwrapProxy) unwrapProxyPropertyNames.Add(property.Name); } - - isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl; SetReflectionOptimizer(); Instantiator = BuildInstantiator(mappedEntity); @@ -299,9 +298,7 @@ public override bool IsLifecycleImplementor get { return islifecycleImplementor; } } -#pragma warning disable 672 - protected override void SetValue(object entity, int i, object value) -#pragma warning restore 672 + public override void SetPropertyValue(object entity, int i, object value) { if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null) { @@ -309,9 +306,7 @@ protected override void SetValue(object entity, int i, object value) return; } -#pragma warning disable 618 - base.SetValue(entity, i, value); -#pragma warning restore 618 + base.SetPropertyValue(entity, i, value); } public override void SetPropertyValues(object entity, object[] values) From 9cbbcc28db9d53c5dafee8598c536579ade558d8 Mon Sep 17 00:00:00 2001 From: maca88 Date: Sun, 23 Dec 2018 13:54:32 +0100 Subject: [PATCH 3/8] Fixed formatting --- src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs b/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs index 280f5730be4..09877c20409 100644 --- a/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs +++ b/src/NHibernate/Bytecode/Lightweight/BytecodeProviderImpl.cs @@ -39,8 +39,8 @@ public override IReflectionOptimizer GetReflectionOptimizer(System.Type mappedCl /// The specialized getter for the given type. /// The specialized setter for the given type. /// The reflection optimization delegate. - internal IReflectionOptimizer GetReflectionOptimizer(System.Type mappedClass, IGetter[] getters, ISetter[] setters, - IGetter specializedGetter, ISetter specializedSetter) + internal IReflectionOptimizer GetReflectionOptimizer( + System.Type mappedClass, IGetter[] getters, ISetter[] setters, IGetter specializedGetter, ISetter specializedSetter) { return new ReflectionOptimizer(mappedClass, getters, setters, specializedGetter, specializedSetter); } From edf7265d5ab5cbd5668a85ee28f58514dbf37bcf Mon Sep 17 00:00:00 2001 From: maca88 Date: Sun, 23 Dec 2018 15:46:14 +0100 Subject: [PATCH 4/8] Removed Getter/Setter in favor of GetPropertyValueInvoker and SetPropertyValueInvoker delegates --- .../Bytecode/Lightweight/AccessOptimizer.cs | 49 ++++++++----------- .../Bytecode/Lightweight/Delegates.cs | 4 ++ src/NHibernate/Bytecode/Lightweight/Getter.cs | 31 ------------ .../Lightweight/ReflectionOptimizer.cs | 20 ++++---- src/NHibernate/Bytecode/Lightweight/Setter.cs | 31 ------------ 5 files changed, 34 insertions(+), 101 deletions(-) delete mode 100644 src/NHibernate/Bytecode/Lightweight/Getter.cs delete mode 100644 src/NHibernate/Bytecode/Lightweight/Setter.cs diff --git a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs index 651d95752eb..1ebe72ab38f 100644 --- a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs @@ -11,20 +11,23 @@ public class AccessOptimizer : IAccessOptimizer private readonly SetPropertyValuesInvoker setDelegate; private readonly GetterCallback getterCallback; private readonly SetterCallback setterCallback; - private readonly Getter[] _getters; - private readonly Setter[] _setters; - private readonly Getter _specializedGetter; - private readonly Setter _specializedSetter; + private readonly GetPropertyValueInvoker[] _getters; + private readonly SetPropertyValueInvoker[] _setters; + private readonly GetPropertyValueInvoker _specializedGetter; + private readonly SetPropertyValueInvoker _specializedSetter; // Since 5.3 [Obsolete("This constructor has no usages and will be removed in a future version")] - public AccessOptimizer(GetPropertyValuesInvoker getDelegate, SetPropertyValuesInvoker setDelegate, - IGetter[] getters, ISetter[] setters) + public AccessOptimizer( + GetPropertyValuesInvoker getDelegate, + SetPropertyValuesInvoker setDelegate, + IGetter[] getters, + ISetter[] setters) : this( getDelegate, - setDelegate, - getters.Select(o => new Getter(o)).ToArray(), - setters.Select(o => new Setter(o)).ToArray(), + setDelegate, + getters.Select(o => (GetPropertyValueInvoker) o.Get).ToArray(), + setters.Select(o => (SetPropertyValueInvoker) o.Set).ToArray(), null, null) { @@ -32,10 +35,10 @@ public AccessOptimizer(GetPropertyValuesInvoker getDelegate, SetPropertyValuesIn public AccessOptimizer(GetPropertyValuesInvoker getDelegate, SetPropertyValuesInvoker setDelegate, - Getter[] getters, - Setter[] setters, - Getter specializedGetter, - Setter specializedSetter) + GetPropertyValueInvoker[] getters, + SetPropertyValueInvoker[] setters, + GetPropertyValueInvoker specializedGetter, + SetPropertyValueInvoker specializedSetter) { this.getDelegate = getDelegate; this.setDelegate = setDelegate; @@ -77,26 +80,14 @@ internal object GetSpecializedPropertyValue(object target) return GetPropertyValue(target, _specializedGetter); } - private static object GetPropertyValue(object target, Getter getter) + private static object GetPropertyValue(object target, GetPropertyValueInvoker getter) { - if (getter.Optimized == null) - { - return getter.Default.Get(target); - } - - return getter.Optimized(target); + return getter(target); } - private static void SetPropertyValue(object target, object value, Setter setter) + private static void SetPropertyValue(object target, object value, SetPropertyValueInvoker setter) { - if (setter.Optimized == null) - { - setter.Default.Set(target, value); - } - else - { - setter.Optimized(target, value); - } + setter(target, value); } } } diff --git a/src/NHibernate/Bytecode/Lightweight/Delegates.cs b/src/NHibernate/Bytecode/Lightweight/Delegates.cs index 7e7ef3cddef..69883433e9b 100644 --- a/src/NHibernate/Bytecode/Lightweight/Delegates.cs +++ b/src/NHibernate/Bytecode/Lightweight/Delegates.cs @@ -6,7 +6,11 @@ namespace NHibernate.Bytecode.Lightweight public delegate object[] GetPropertyValuesInvoker(object obj, GetterCallback callback); + public delegate object GetPropertyValueInvoker(object obj); + public delegate void SetPropertyValuesInvoker(object obj, object[] values, SetterCallback callback); + public delegate void SetPropertyValueInvoker(object obj, object value); + public delegate object CreateInstanceInvoker(); } diff --git a/src/NHibernate/Bytecode/Lightweight/Getter.cs b/src/NHibernate/Bytecode/Lightweight/Getter.cs deleted file mode 100644 index 50a2bbf4ed8..00000000000 --- a/src/NHibernate/Bytecode/Lightweight/Getter.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using NHibernate.Properties; - -namespace NHibernate.Bytecode.Lightweight -{ - /// - /// Contains the instance with an optional optimized delegate. - /// - public class Getter - { - public Getter(IGetter @default) : this(@default, null) - { - } - - public Getter(IGetter @default, Func optimized) - { - Default = @default; - Optimized = optimized; - } - - /// - /// The default instance. - /// - public IGetter Default { get; } - - /// - /// Optimized getter delegate. - /// - public Func Optimized { get; } - } -} diff --git a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs index 6aa105d6671..1013ae56913 100644 --- a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs @@ -56,16 +56,16 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] GetPropertyValuesInvoker getInvoker = GenerateGetPropertyValuesMethod(getters); SetPropertyValuesInvoker setInvoker = GenerateSetPropertyValuesMethod(setters); - var getMethods = new Getter[getters.Length]; + var getMethods = new GetPropertyValueInvoker[getters.Length]; for (var i = 0; i < getters.Length; i++) { - getMethods[i] = new Getter(getters[i], GenerateGetPropertyValueMethod(getters[i])); + getMethods[i] = GenerateGetPropertyValueMethod(getters[i]) ?? getters[i].Get; } - var setMethods = new Setter[setters.Length]; + var setMethods = new SetPropertyValueInvoker[setters.Length]; for (var i = 0; i < setters.Length; i++) { - setMethods[i] = new Setter(setters[i], GenerateSetPropertyValueMethod(setters[i])); + setMethods[i] = GenerateSetPropertyValueMethod(setters[i]) ?? setters[i].Set; } accessOptimizer = new AccessOptimizer( @@ -73,8 +73,8 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setInvoker, getMethods, setMethods, - new Getter(specializedGetter, GenerateGetPropertyValueMethod(specializedGetter)), - new Setter(specializedSetter, GenerateSetPropertyValueMethod(specializedSetter)) + GenerateGetPropertyValueMethod(specializedGetter), + GenerateSetPropertyValueMethod(specializedSetter) ); createInstanceMethod = CreateCreateInstanceMethod(mappedType); @@ -162,7 +162,7 @@ private static void EmitCastToReference(ILGenerator il, System.Type type) private static readonly MethodInfo GetterCallbackInvoke = ReflectHelper.GetMethod( g => g.Invoke(null, 0)); - private Func GenerateGetPropertyValueMethod(IGetter getter) + private GetPropertyValueInvoker GenerateGetPropertyValueMethod(IGetter getter) { if (!(getter is IOptimizableGetter optimizableGetter)) { @@ -179,10 +179,10 @@ private Func GenerateGetPropertyValueMethod(IGetter getter) EmitUtil.EmitBoxIfNeeded(il, getter.ReturnType); il.Emit(OpCodes.Ret); - return (Func) method.CreateDelegate(typeof(Func)); + return (GetPropertyValueInvoker) method.CreateDelegate(typeof(GetPropertyValueInvoker)); } - private Action GenerateSetPropertyValueMethod(ISetter setter) + private SetPropertyValueInvoker GenerateSetPropertyValueMethod(ISetter setter) { if (!(setter is IOptimizableSetter optimizableSetter)) { @@ -199,7 +199,7 @@ private Action GenerateSetPropertyValueMethod(ISetter setter) optimizableSetter.Emit(il); il.Emit(OpCodes.Ret); - return (Action) method.CreateDelegate(typeof(Action)); + return (SetPropertyValueInvoker) method.CreateDelegate(typeof(SetPropertyValueInvoker)); } /// diff --git a/src/NHibernate/Bytecode/Lightweight/Setter.cs b/src/NHibernate/Bytecode/Lightweight/Setter.cs deleted file mode 100644 index cd45686e0d6..00000000000 --- a/src/NHibernate/Bytecode/Lightweight/Setter.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using NHibernate.Properties; - -namespace NHibernate.Bytecode.Lightweight -{ - /// - /// Contains the instance with an optional optimized delegate. - /// - public class Setter - { - public Setter(ISetter @default) : this(@default, null) - { - } - - public Setter(ISetter @default, Action optimized) - { - Default = @default; - Optimized = optimized; - } - - /// - /// The default instance. - /// - public ISetter Default { get; } - - /// - /// Optimized setter delegate. - /// - public Action Optimized { get; } - } -} From 603982372b48bacff82a21241991f03fb915b173 Mon Sep 17 00:00:00 2001 From: maca88 Date: Tue, 25 Dec 2018 19:21:48 +0100 Subject: [PATCH 5/8] Removed unneeded methods from AccessOptimizer and corrected serialization for isBytecodeProviderImpl field --- .../Bytecode/Lightweight/AccessOptimizer.cs | 18 ++++-------------- .../Tuple/Component/PocoComponentTuplizer.cs | 1 + .../Tuple/Entity/PocoEntityTuplizer.cs | 1 + 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs index 1ebe72ab38f..cf79e2d0c82 100644 --- a/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs @@ -62,32 +62,22 @@ public void SetPropertyValues(object target, object[] values) public void SetPropertyValue(object target, int i, object value) { - SetPropertyValue(target, value, _setters[i]); + _setters[i](target, value); } public object GetPropertyValue(object target, int i) { - return GetPropertyValue(target, _getters[i]); + return _getters[i](target); } internal void SetSpecializedPropertyValue(object target, object value) { - SetPropertyValue(target, value, _specializedSetter); + _specializedSetter(target, value); } internal object GetSpecializedPropertyValue(object target) { - return GetPropertyValue(target, _specializedGetter); - } - - private static object GetPropertyValue(object target, GetPropertyValueInvoker getter) - { - return getter(target); - } - - private static void SetPropertyValue(object target, object value, SetPropertyValueInvoker setter) - { - setter(target, value); + return _specializedGetter(target); } } } diff --git a/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs b/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs index e45037e68c8..c7b93ca21a1 100644 --- a/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs +++ b/src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs @@ -19,6 +19,7 @@ public class PocoComponentTuplizer : AbstractComponentTuplizer private readonly IGetter parentGetter; [NonSerialized] private IReflectionOptimizer optimizer; + [NonSerialized] private bool isBytecodeProviderImpl; // 6.0 TODO: remove diff --git a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs index ae310903873..4b9b1bfebc6 100644 --- a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs +++ b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs @@ -30,6 +30,7 @@ public class PocoEntityTuplizer : AbstractEntityTuplizer [NonSerialized] private IReflectionOptimizer optimizer; private readonly IProxyValidator proxyValidator; + [NonSerialized] private bool isBytecodeProviderImpl; // 6.0 TODO: remove [OnDeserialized] From 53cdcfcfcb561e8bfe3842c39d79911190ad07f7 Mon Sep 17 00:00:00 2001 From: maca88 Date: Wed, 26 Dec 2018 18:59:06 +0100 Subject: [PATCH 6/8] Added a fallback for specialized getter/setter --- .../Bytecode/Lightweight/ReflectionOptimizer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs index 1013ae56913..ea7b88d8227 100644 --- a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs @@ -73,8 +73,13 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setInvoker, getMethods, setMethods, - GenerateGetPropertyValueMethod(specializedGetter), - GenerateSetPropertyValueMethod(specializedSetter) + // 6.0 TODO: Remove ternary ifs once the obsolete constructor is removed + specializedGetter != null + ? GenerateGetPropertyValueMethod(specializedGetter) ?? specializedGetter.Get + : null, + specializedSetter != null + ? GenerateSetPropertyValueMethod(specializedSetter) ?? specializedSetter.Set + : null ); createInstanceMethod = CreateCreateInstanceMethod(mappedType); From 3988a2139a59b00f40fa39d37f0af58a602087b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sun, 30 Dec 2018 16:26:20 +0100 Subject: [PATCH 7/8] Simplify fallback --- .../Lightweight/ReflectionOptimizer.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs index ea7b88d8227..80d45a99f3e 100644 --- a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Security; using System.Security.Permissions; -using NHibernate.Linq; using NHibernate.Properties; using NHibernate.Util; @@ -44,8 +42,9 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] /// /// Class constructor. /// - public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setters, - IGetter specializedGetter, ISetter specializedSetter) + public ReflectionOptimizer( + System.Type mappedType, IGetter[] getters, ISetter[] setters, + IGetter specializedGetter, ISetter specializedSetter) { // save off references this.mappedType = mappedType; @@ -59,13 +58,13 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] var getMethods = new GetPropertyValueInvoker[getters.Length]; for (var i = 0; i < getters.Length; i++) { - getMethods[i] = GenerateGetPropertyValueMethod(getters[i]) ?? getters[i].Get; + getMethods[i] = GenerateGetPropertyValueMethod(getters[i]); } var setMethods = new SetPropertyValueInvoker[setters.Length]; for (var i = 0; i < setters.Length; i++) { - setMethods[i] = GenerateSetPropertyValueMethod(setters[i]) ?? setters[i].Set; + setMethods[i] = GenerateSetPropertyValueMethod(setters[i]); } accessOptimizer = new AccessOptimizer( @@ -73,13 +72,8 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setInvoker, getMethods, setMethods, - // 6.0 TODO: Remove ternary ifs once the obsolete constructor is removed - specializedGetter != null - ? GenerateGetPropertyValueMethod(specializedGetter) ?? specializedGetter.Get - : null, - specializedSetter != null - ? GenerateSetPropertyValueMethod(specializedSetter) ?? specializedSetter.Set - : null + GenerateGetPropertyValueMethod(specializedGetter), + GenerateSetPropertyValueMethod(specializedSetter) ); createInstanceMethod = CreateCreateInstanceMethod(mappedType); @@ -171,7 +165,7 @@ private GetPropertyValueInvoker GenerateGetPropertyValueMethod(IGetter getter) { if (!(getter is IOptimizableGetter optimizableGetter)) { - return null; + return getter.Get; } var method = CreateDynamicMethod(typeof(object), new[] { typeof(object) }); @@ -191,7 +185,7 @@ private SetPropertyValueInvoker GenerateSetPropertyValueMethod(ISetter setter) { if (!(setter is IOptimizableSetter optimizableSetter)) { - return null; + return setter.Set; } // void (target, value) { ((ClassType) target).SetMethod((FieldType) value); } From 146d392329889d2d4178acf272063acb54d58720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Mon, 31 Dec 2018 14:34:05 +0100 Subject: [PATCH 8/8] Fix fallback simplification --- .../Bytecode/Lightweight/ReflectionOptimizer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs index 80d45a99f3e..634872e25b7 100644 --- a/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs +++ b/src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs @@ -38,7 +38,7 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] : this(mappedType, getters, setters, null, null) { } - + /// /// Class constructor. /// @@ -163,10 +163,10 @@ private static void EmitCastToReference(ILGenerator il, System.Type type) private GetPropertyValueInvoker GenerateGetPropertyValueMethod(IGetter getter) { + if (getter == null) + return null; if (!(getter is IOptimizableGetter optimizableGetter)) - { return getter.Get; - } var method = CreateDynamicMethod(typeof(object), new[] { typeof(object) }); var il = method.GetILGenerator(); @@ -183,10 +183,10 @@ private GetPropertyValueInvoker GenerateGetPropertyValueMethod(IGetter getter) private SetPropertyValueInvoker GenerateSetPropertyValueMethod(ISetter setter) { + if (setter == null) + return null; if (!(setter is IOptimizableSetter optimizableSetter)) - { return setter.Set; - } // void (target, value) { ((ClassType) target).SetMethod((FieldType) value); } var method = CreateDynamicMethod(null, new[] { typeof(object), typeof(object) });