diff --git a/src/NHibernate.Test/Bytecode/Lightweight/BytecodeProviderFixture.cs b/src/NHibernate.Test/Bytecode/Lightweight/BytecodeProviderFixture.cs index a721fc6f2e6..31d4ca7432e 100644 --- a/src/NHibernate.Test/Bytecode/Lightweight/BytecodeProviderFixture.cs +++ b/src/NHibernate.Test/Bytecode/Lightweight/BytecodeProviderFixture.cs @@ -14,7 +14,7 @@ public void NotConfiguredProxyFactoryFactory() { var bcp = new BytecodeProviderImpl(); IProxyFactoryFactory p = bcp.ProxyFactoryFactory; - Assert.That(p, Is.InstanceOf()); + Assert.That(p, Is.InstanceOf()); } [Test] diff --git a/src/NHibernate/Bytecode/AbstractBytecodeProvider.cs b/src/NHibernate/Bytecode/AbstractBytecodeProvider.cs index 16c7fcd6468..8c6f19a7da2 100644 --- a/src/NHibernate/Bytecode/AbstractBytecodeProvider.cs +++ b/src/NHibernate/Bytecode/AbstractBytecodeProvider.cs @@ -28,7 +28,8 @@ public virtual IProxyFactoryFactory ProxyFactoryFactory throw new HibernateByteCodeException("Failed to create an instance of '" + proxyFactoryFactory.FullName + "'!", e); } } - return new DefaultProxyFactoryFactory(); + + return StaticProxyFactoryFactory.Instance; } } @@ -116,4 +117,4 @@ public void SetCollectionTypeFactoryClass(System.Type type) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Bytecode/StaticProxyFactoryFactory.cs b/src/NHibernate/Bytecode/StaticProxyFactoryFactory.cs new file mode 100644 index 00000000000..d3d9a912f15 --- /dev/null +++ b/src/NHibernate/Bytecode/StaticProxyFactoryFactory.cs @@ -0,0 +1,17 @@ +using NHibernate.Proxy; + +namespace NHibernate.Bytecode +{ + public class StaticProxyFactoryFactory : IProxyFactoryFactory + { + internal static StaticProxyFactoryFactory Instance = new StaticProxyFactoryFactory(); + + public IProxyFactory BuildProxyFactory() => new StaticProxyFactory(); + + public IProxyValidator ProxyValidator => new DynProxyTypeValidator(); + + public bool IsInstrumented(System.Type entityClass) => true; + + public bool IsProxy(object entity) => entity is INHibernateProxy; + } +} diff --git a/src/NHibernate/Proxy/DynamicProxy/DefaultProxyMethodBuilder.cs b/src/NHibernate/Proxy/DynamicProxy/DefaultProxyMethodBuilder.cs index c999aa10086..0e8249d2466 100644 --- a/src/NHibernate/Proxy/DynamicProxy/DefaultProxyMethodBuilder.cs +++ b/src/NHibernate/Proxy/DynamicProxy/DefaultProxyMethodBuilder.cs @@ -30,7 +30,7 @@ public DefaultyProxyMethodBuilder(IMethodBodyEmitter emitter) public IMethodBodyEmitter MethodBodyEmitter { get; private set; } - private static MethodBuilder GenerateMethodSignature(string name, MethodInfo method, TypeBuilder typeBuilder) + internal static MethodBuilder GenerateMethodSignature(string name, MethodInfo method, TypeBuilder typeBuilder) { //TODO: Should we use attributes of base method? var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual; diff --git a/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs b/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs index 4f35eb0d35a..a40adbb80bc 100644 --- a/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs +++ b/src/NHibernate/Proxy/DynamicProxy/ProxyFactory.cs @@ -129,7 +129,7 @@ private TypeInfo CreateUncachedProxyType(System.Type baseType, IReadOnlyCollecti return proxyType; } - private IEnumerable GetProxiableMethods(System.Type type, IEnumerable interfaces) + internal static IEnumerable GetProxiableMethods(System.Type type, IEnumerable interfaces) { const BindingFlags candidateMethodsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; return diff --git a/src/NHibernate/Proxy/LiteLazyInitializer.cs b/src/NHibernate/Proxy/LiteLazyInitializer.cs new file mode 100644 index 00000000000..9684bbed5f7 --- /dev/null +++ b/src/NHibernate/Proxy/LiteLazyInitializer.cs @@ -0,0 +1,17 @@ +using System; +using NHibernate.Engine; + +namespace NHibernate.Proxy +{ + [Serializable] + internal sealed class LiteLazyInitializer : AbstractLazyInitializer + { + internal LiteLazyInitializer(string entityName, object id, ISessionImplementor session, System.Type persistentClass) + : base(entityName, id, session) + { + PersistentClass = persistentClass; + } + + public override System.Type PersistentClass { get; } + } +} diff --git a/src/NHibernate/Proxy/NHibernateProxyBuilder.cs b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs new file mode 100644 index 00000000000..bd3613c725a --- /dev/null +++ b/src/NHibernate/Proxy/NHibernateProxyBuilder.cs @@ -0,0 +1,386 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using System.Security; +using NHibernate.Proxy.DynamicProxy; +using NHibernate.Type; +using NHibernate.Util; + +namespace NHibernate.Proxy +{ + internal class NHibernateProxyBuilder + { + private const MethodAttributes constructorAttributes = + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + + private static readonly ConstructorInfo ObjectConstructor = typeof(object).GetConstructor(System.Type.EmptyTypes); + + private static readonly System.Type NHibernateProxyType = typeof(INHibernateProxy); + private static readonly PropertyInfo NHibernateProxyTypeLazyInitializerProperty = NHibernateProxyType.GetProperty(nameof(INHibernateProxy.HibernateLazyInitializer)); + private static readonly System.Type LazyInitializerType = typeof(ILazyInitializer); + private static readonly PropertyInfo LazyInitializerIdentifierProperty = LazyInitializerType.GetProperty(nameof(ILazyInitializer.Identifier)); + private static readonly MethodInfo LazyInitializerInitializeMethod = LazyInitializerType.GetMethod(nameof(ILazyInitializer.Initialize)); + private static readonly MethodInfo LazyInitializerGetImplementationMethod = LazyInitializerType.GetMethod(nameof(ILazyInitializer.GetImplementation), System.Type.EmptyTypes); + private static readonly IProxyAssemblyBuilder ProxyAssemblyBuilder = new DefaultProxyAssemblyBuilder(); + + private static readonly ConstructorInfo SecurityCriticalAttributeConstructor = typeof(SecurityCriticalAttribute).GetConstructor(System.Type.EmptyTypes); + private static readonly MethodInfo SerializableGetObjectDataMethod = typeof(ISerializable).GetMethod(nameof(ISerializable.GetObjectData)); + private static readonly MethodInfo SerializationInfoSetTypeMethod = ReflectHelper.GetMethod(si => si.SetType(null)); + + private readonly MethodInfo _getIdentifierMethod; + private readonly MethodInfo _setIdentifierMethod; + private readonly IAbstractComponentType _componentIdType; + private readonly bool _overridesEquals; + + public NHibernateProxyBuilder(MethodInfo getIdentifierMethod, MethodInfo setIdentifierMethod, IAbstractComponentType componentIdType, bool overridesEquals) + { + _getIdentifierMethod = getIdentifierMethod; + _setIdentifierMethod = setIdentifierMethod; + _componentIdType = componentIdType; + _overridesEquals = overridesEquals; + } + + public TypeInfo CreateProxyType(System.Type baseType, IReadOnlyCollection baseInterfaces) + { + var typeName = $"{baseType.Name}Proxy"; + var assemblyName = $"{typeName}Assembly"; + var moduleName = $"{typeName}Module"; + + var name = new AssemblyName(assemblyName); + + var assemblyBuilder = ProxyAssemblyBuilder.DefineDynamicAssembly(AppDomain.CurrentDomain, name); + var moduleBuilder = ProxyAssemblyBuilder.DefineDynamicModule(assemblyBuilder, moduleName); + + const TypeAttributes typeAttributes = TypeAttributes.AutoClass | TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.BeforeFieldInit; + + var interfaces = new HashSet + { + // Add the ISerializable interface so that it can be implemented + typeof(ISerializable) + }; + interfaces.UnionWith(baseInterfaces); + interfaces.UnionWith(baseInterfaces.SelectMany(i => i.GetInterfaces())); + interfaces.UnionWith(baseType.GetInterfaces()); + + // Use the object as the base type + // since we're not inheriting from any class type + var parentType = baseType; + if (baseType.IsInterface) + { + parentType = typeof(object); + interfaces.Add(baseType); + } + + var typeBuilder = moduleBuilder.DefineType(typeName, typeAttributes, parentType, interfaces.ToArray()); + + var lazyInitializerField = typeBuilder.DefineField("__lazyInitializer", LazyInitializerType, FieldAttributes.Private); + var proxyInfoField = typeBuilder.DefineField("__proxyInfo", typeof(NHibernateProxyFactoryInfo), FieldAttributes.Private); + + ImplementConstructor(typeBuilder, parentType, lazyInitializerField, proxyInfoField); + + // Provide a custom implementation of ISerializable + // instead of redirecting it back to the interceptor + foreach (var method in ProxyFactory.GetProxiableMethods(baseType, interfaces.Except(new[] {typeof(ISerializable)}))) + { + CreateProxiedMethod(typeBuilder, method, lazyInitializerField); + } + + // Make the proxy serializable + var serializableConstructor = typeof(SerializableAttribute).GetConstructor(System.Type.EmptyTypes); + var customAttributeBuilder = new CustomAttributeBuilder(serializableConstructor, new object[0]); + typeBuilder.SetCustomAttribute(customAttributeBuilder); + + ImplementDeserializationConstructor(typeBuilder); + ImplementGetObjectData(typeBuilder, proxyInfoField, lazyInitializerField); + + var proxyType = typeBuilder.CreateTypeInfo(); + + ProxyAssemblyBuilder.Save(assemblyBuilder); + + return proxyType; + } + + private void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField) + { + if (method == NHibernateProxyTypeLazyInitializerProperty.GetMethod) + { + ImplementGetLazyInitializer(typeBuilder, method, lazyInitializerField); + } + else if (method == _getIdentifierMethod) + { + ImplementGetIdentifier(typeBuilder, method, lazyInitializerField); + } + else if (method == _setIdentifierMethod) + { + ImplementSetIdentifier(typeBuilder, method, lazyInitializerField); + } + else if (!_overridesEquals && method.Name == "Equals" && method.GetBaseDefinition() == typeof(object).GetMethod("Equals", new[] {typeof(object)})) + { + //skip + } + else if (!_overridesEquals && method.Name == "GetHashCode" && method.GetBaseDefinition() == typeof(object).GetMethod("GetHashCode")) + { + //skip + } + else if (_componentIdType != null && _componentIdType.IsMethodOf(method)) + { + ImplementCallMethodOnEmbeddedComponentId(typeBuilder, method, lazyInitializerField); + } + else + { + ImplementCallMethodOnImplementation(typeBuilder, method, lazyInitializerField); + } + } + + private static void ImplementConstructor(TypeBuilder typeBuilder, System.Type parentType, FieldInfo lazyInitializerField, FieldInfo proxyInfoField) + { + var constructor = typeBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard, new[] {LazyInitializerType, typeof(NHibernateProxyFactoryInfo)}); + + var baseConstructor = parentType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, System.Type.EmptyTypes, null); + + // if there is no default constructor, or the default constructor is private/internal, call System.Object constructor + // this works, but the generated assembly will fail PeVerify (cannot use in medium trust for example) + if (baseConstructor == null || baseConstructor.IsPrivate || baseConstructor.IsAssembly) + baseConstructor = ObjectConstructor; + + var IL = constructor.GetILGenerator(); + + constructor.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed); + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Call, baseConstructor); + + // __lazyInitializer == lazyInitializer; + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldarg_1); + IL.Emit(OpCodes.Stfld, lazyInitializerField); + + // __proxyInfo == proxyInfo; + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldarg_2); + IL.Emit(OpCodes.Stfld, proxyInfoField); + + IL.Emit(OpCodes.Ret); + } + + private static void ImplementDeserializationConstructor(TypeBuilder typeBuilder) + { + var parameterTypes = new[] {typeof (SerializationInfo), typeof (StreamingContext)}; + var constructor = typeBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard, parameterTypes); + constructor.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed); + + var IL = constructor.GetILGenerator(); + //Everything is done in NHibernateProxyObjectReference, so just return data. + IL.Emit(OpCodes.Ret); + } + + private static void ImplementGetObjectData(TypeBuilder typeBuilder, FieldInfo proxyInfoField, FieldInfo lazyInitializerField) + { + const MethodAttributes attributes = + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual; + + var parameterTypes = new[] {typeof (SerializationInfo), typeof (StreamingContext)}; + + var methodBuilder = typeBuilder.DefineMethod("GetObjectData", attributes, typeof (void), parameterTypes); + methodBuilder.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed); + methodBuilder.SetCustomAttribute(new CustomAttributeBuilder(SecurityCriticalAttributeConstructor, Array.Empty())); + + var IL = methodBuilder.GetILGenerator(); + //LocalBuilder proxyBaseType = IL.DeclareLocal(typeof(Type)); + + // info.SetType(typeof(NHibernateProxyObjectReference)); + IL.Emit(OpCodes.Ldarg_1); + IL.Emit(OpCodes.Ldtoken, typeof (NHibernateProxyObjectReference)); + IL.Emit(OpCodes.Call, ReflectionCache.TypeMethods.GetTypeFromHandle); + IL.Emit(OpCodes.Callvirt, SerializationInfoSetTypeMethod); + + // (new NHibernateProxyObjectReference(this.__proxyInfo, this.__lazyInitializer.Identifier)).GetObjectData(info, context); + //this.__proxyInfo + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, proxyInfoField); + + //this.__lazyInitializer.Identifier + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Callvirt, LazyInitializerIdentifierProperty.GetMethod); + + var constructor = typeof(NHibernateProxyObjectReference).GetConstructor( + new[] + { + typeof(NHibernateProxyFactoryInfo), + typeof(object), + }); + IL.Emit(OpCodes.Newobj, constructor); + + IL.Emit(OpCodes.Ldarg_1); + IL.Emit(OpCodes.Ldarg_2); + IL.Emit(OpCodes.Callvirt, SerializableGetObjectDataMethod); + + IL.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodBuilder, SerializableGetObjectDataMethod); + } + + private static void ImplementGetLazyInitializer(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField) + { + // get { return this.__lazyInitializer; } + + const MethodAttributes attributes = + MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | + MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual; + + var getMethod = typeBuilder.DefineMethod($"{NHibernateProxyType.FullName}.get_{nameof(INHibernateProxy.HibernateLazyInitializer)}", attributes, CallingConventions.HasThis, LazyInitializerType, System.Type.EmptyTypes); + getMethod.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.IL); + + var IL = getMethod.GetILGenerator(); + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(getMethod, method); + } + + private static void ImplementGetIdentifier(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField) + { + /* + get + { + if (this.__lazyInitializer == null) + return base.get_(); + return ()this.__lazyInitializer.Identifier; + } + */ + var methodOverride = DefaultyProxyMethodBuilder.GenerateMethodSignature(method.Name, method, typeBuilder); + + var IL = methodOverride.GetILGenerator(); + + EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField); + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Callvirt, LazyInitializerIdentifierProperty.GetMethod); + IL.Emit(OpCodes.Unbox_Any, method.ReturnType); + IL.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodOverride, method); + } + + private static void ImplementSetIdentifier(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField) + { + /* + set + { + if (this.__lazyInitializer == null) + return base.set_(value); + this.__lazyInitializer.Initialize(); + this.__lazyInitializer.Identifier = value; + this.__lazyInitializer.GetImplementation(). = value; + } + */ + var propertyType = method.GetParameters()[0].ParameterType; + var methodOverride = DefaultyProxyMethodBuilder.GenerateMethodSignature(method.Name, method, typeBuilder); + var IL = methodOverride.GetILGenerator(); + + EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField); + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Callvirt, LazyInitializerInitializeMethod); + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Ldarg_1); + if (propertyType.IsValueType) + IL.Emit(OpCodes.Box, propertyType); + IL.Emit(OpCodes.Callvirt, LazyInitializerIdentifierProperty.SetMethod); + + EmitCallImplementation(IL, method, lazyInitializerField); + + IL.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodOverride, method); + } + + private static void ImplementCallMethodOnEmbeddedComponentId(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField) + { + /* + if (this.__lazyInitializer == null) + return base.(args..); + this.__lazyInitializer.Identifier.(args..); + */ + var methodOverride = DefaultyProxyMethodBuilder.GenerateMethodSignature(method.Name, method, typeBuilder); + + var IL = methodOverride.GetILGenerator(); + + EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField); + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Callvirt, LazyInitializerIdentifierProperty.GetMethod); + IL.Emit(OpCodes.Unbox_Any, method.DeclaringType); + EmitCallMethod(IL, OpCodes.Callvirt, method); + IL.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodOverride, method); + } + + private static void ImplementCallMethodOnImplementation(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField) + { + /* + if (this.__lazyInitializer == null) + return base.(args..); + return this.__lazyInitializer.GetImplementation().(args..) + */ + var methodOverride = DefaultyProxyMethodBuilder.GenerateMethodSignature(method.Name, method, typeBuilder); + + var IL = methodOverride.GetILGenerator(); + + EmitCallBaseIfLazyInitializerIsNull(IL, method, lazyInitializerField); + + EmitCallImplementation(IL, method, lazyInitializerField); + IL.Emit(OpCodes.Ret); + + typeBuilder.DefineMethodOverride(methodOverride, method); + } + + private static void EmitCallBaseIfLazyInitializerIsNull(ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField) + { + //if (this.__lazyInitializer == null) + // return base.(args..) + + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + var skipBaseCall = IL.DefineLabel(); + + IL.Emit(OpCodes.Ldnull); + IL.Emit(OpCodes.Bne_Un, skipBaseCall); + + IL.Emit(OpCodes.Ldarg_0); + EmitCallMethod(IL, OpCodes.Call, method); + IL.Emit(OpCodes.Ret); + + IL.MarkLabel(skipBaseCall); + } + + private static void EmitCallMethod(ILGenerator IL, OpCode opCode, MethodInfo method) + { + for (var i = 0; i < method.GetParameters().Length; i++) + IL.Emit(OpCodes.Ldarg_S, (sbyte) (1 + i)); + IL.Emit(opCode, method); + } + + private static void EmitCallImplementation(ILGenerator IL, MethodInfo method, FieldInfo lazyInitializerField) + { + //(()this.__lazyInitializer.GetImplementation()).(args..); + IL.Emit(OpCodes.Ldarg_0); + IL.Emit(OpCodes.Ldfld, lazyInitializerField); + IL.Emit(OpCodes.Callvirt, LazyInitializerGetImplementationMethod); + IL.Emit(OpCodes.Unbox_Any, method.DeclaringType); + EmitCallMethod(IL, OpCodes.Callvirt, method); + } + } +} diff --git a/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs b/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs new file mode 100644 index 00000000000..065c3705729 --- /dev/null +++ b/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security; +using NHibernate.Type; + +namespace NHibernate.Proxy +{ + [Serializable] + public sealed class NHibernateProxyFactoryInfo : ISerializable + { + private readonly string _entityName; + private readonly System.Type _persistentClass; + private readonly System.Type[] _interfaces; + private readonly MethodInfo _getIdentifierMethod; + private readonly MethodInfo _setIdentifierMethod; + private readonly IAbstractComponentType _componentIdType; + + internal NHibernateProxyFactoryInfo(string entityName, System.Type persistentClass, System.Type[] interfaces, MethodInfo getIdentifierMethod, MethodInfo setIdentifierMethod, IAbstractComponentType componentIdType) + { + _entityName = entityName; + _persistentClass = persistentClass; + _interfaces = interfaces; + _getIdentifierMethod = getIdentifierMethod; + _setIdentifierMethod = setIdentifierMethod; + _componentIdType = componentIdType; + } + + public IProxyFactory CreateProxyFactory() + { + var factory = new StaticProxyFactory(); + factory.PostInstantiate(_entityName, _persistentClass, new HashSet(_interfaces), _getIdentifierMethod, _setIdentifierMethod, _componentIdType); + return factory; + } + + private NHibernateProxyFactoryInfo(SerializationInfo info, StreamingContext context) + { + _entityName = (string) info.GetValue(nameof(_entityName), typeof(string)); + _persistentClass = (System.Type) info.GetValue(nameof(_persistentClass), typeof(System.Type)); + _interfaces = (System.Type[]) info.GetValue(nameof(_interfaces), typeof(System.Type[])); + _getIdentifierMethod = (MethodInfo) info.GetValue(nameof(_getIdentifierMethod), typeof(MethodInfo)); + _setIdentifierMethod = (MethodInfo) info.GetValue(nameof(_setIdentifierMethod), typeof(MethodInfo)); + _componentIdType = (IAbstractComponentType) info.GetValue(nameof(_componentIdType), typeof(IAbstractComponentType)); + } + + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue(nameof(_entityName), _entityName); + info.AddValue(nameof(_persistentClass), _persistentClass); + info.AddValue(nameof(_interfaces), _interfaces); + info.AddValue(nameof(_getIdentifierMethod), _getIdentifierMethod); + info.AddValue(nameof(_setIdentifierMethod), _setIdentifierMethod); + info.AddValue(nameof(_componentIdType), _componentIdType); + } + } +} diff --git a/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs b/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs new file mode 100644 index 00000000000..9878bc5bc21 --- /dev/null +++ b/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs @@ -0,0 +1,38 @@ +using System; +using System.Runtime.Serialization; +using System.Security; + +namespace NHibernate.Proxy +{ + [Serializable] + public sealed class NHibernateProxyObjectReference : IObjectReference, ISerializable + { + private readonly NHibernateProxyFactoryInfo _proxyFactoryInfo; + private readonly object _identifier; + + public NHibernateProxyObjectReference(NHibernateProxyFactoryInfo proxyFactoryInfo, object identifier) + { + _proxyFactoryInfo = proxyFactoryInfo; + _identifier = identifier; + } + + private NHibernateProxyObjectReference(SerializationInfo info, StreamingContext context) + { + _proxyFactoryInfo = (NHibernateProxyFactoryInfo) info.GetValue(nameof(_proxyFactoryInfo), typeof(NHibernateProxyFactoryInfo)); + _identifier = info.GetValue(nameof(_identifier), typeof(object)); + } + + [SecurityCritical] + public object GetRealObject(StreamingContext context) + { + return _proxyFactoryInfo.CreateProxyFactory().GetProxy(_identifier, null); + } + + [SecurityCritical] + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue(nameof(_proxyFactoryInfo), _proxyFactoryInfo); + info.AddValue(nameof(_identifier), _identifier); + } + } +} diff --git a/src/NHibernate/Proxy/StaticProxyFactory.cs b/src/NHibernate/Proxy/StaticProxyFactory.cs new file mode 100644 index 00000000000..d053a93abd2 --- /dev/null +++ b/src/NHibernate/Proxy/StaticProxyFactory.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using NHibernate.Engine; +using NHibernate.Intercept; +using NHibernate.Proxy.DynamicProxy; + +namespace NHibernate.Proxy +{ + public sealed class StaticProxyFactory : AbstractProxyFactory + { + private static readonly ConcurrentDictionary> + Cache = new ConcurrentDictionary>(); + + private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof(StaticProxyFactory)); + + public override INHibernateProxy GetProxy(object id, ISessionImplementor session) + { + try + { + var cacheEntry = new ProxyCacheEntry(IsClassProxy ? PersistentClass : typeof(object), Interfaces); + var proxyActivator = Cache.GetOrAdd(cacheEntry, pke => CreateProxyActivator(pke)); + return proxyActivator( + new LiteLazyInitializer(EntityName, id, session, PersistentClass), + new NHibernateProxyFactoryInfo(EntityName, PersistentClass, Interfaces, GetIdentifierMethod, SetIdentifierMethod, ComponentIdType)); + } + catch (Exception ex) + { + Log.Error(ex, "Creating a proxy instance failed"); + throw new HibernateException("Creating a proxy instance failed", ex); + } + } + + private Func CreateProxyActivator(ProxyCacheEntry pke) + { + var proxyBuilder = new NHibernateProxyBuilder(GetIdentifierMethod, SetIdentifierMethod, ComponentIdType, OverridesEquals); + var type = proxyBuilder.CreateProxyType(pke.BaseType, pke.Interfaces); + var ctor = type.GetConstructor(new[] {typeof(ILazyInitializer), typeof(NHibernateProxyFactoryInfo)}); + var li = Expression.Parameter(typeof(ILazyInitializer)); + var pf = Expression.Parameter(typeof(NHibernateProxyFactoryInfo)); + return Expression.Lambda>(Expression.New(ctor, li, pf), li, pf).Compile(); + } + + public override object GetFieldInterceptionProxy(object instanceToWrap) + { + var factory = new ProxyFactory(); + var interceptor = new DefaultDynamicLazyFieldInterceptor(); + return factory.CreateProxy(PersistentClass, interceptor, typeof(IFieldInterceptorAccessor)); + } + } +}