/src/NHibernate/Util/SerializationHelper.Surrogates.cs

https://github.com/RogerKratz/nhibernate-core · C# · 309 lines · 264 code · 43 blank · 2 comment · 35 complexity · 3ba0f05fe6b861e94a7365566387b681 MD5 · raw file

  1. #if !NETFX
  2. using System;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Runtime.Serialization;
  6. namespace NHibernate.Util
  7. {
  8. public static partial class SerializationHelper
  9. {
  10. private sealed class TypeSerializationSurrogate : ISerializationSurrogate
  11. {
  12. public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
  13. {
  14. info.SetType(typeof(TypeReference));
  15. new TypeReference((System.Type) obj).GetObjectData(info, context);
  16. }
  17. public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) =>
  18. throw new NotSupportedException();
  19. }
  20. [Serializable]
  21. private sealed class TypeReference : IObjectReference, ISerializable
  22. {
  23. private readonly string _assemblyName;
  24. private readonly string _fullName;
  25. public TypeReference(System.Type type)
  26. {
  27. if (type == null)
  28. throw new ArgumentNullException(nameof(type));
  29. _assemblyName = type.Assembly.FullName;
  30. _fullName = type.FullName;
  31. }
  32. private TypeReference(SerializationInfo info, StreamingContext context)
  33. {
  34. _assemblyName = info.GetString("AssemblyName");
  35. _fullName = info.GetString("FullName");
  36. }
  37. public void GetObjectData(SerializationInfo info, StreamingContext context)
  38. {
  39. info.AddValue("AssemblyName", _assemblyName);
  40. info.AddValue("FullName", _fullName);
  41. }
  42. public object GetRealObject(StreamingContext context) =>
  43. Assembly.Load(_assemblyName).GetType(_fullName, true);
  44. }
  45. private sealed class MemberInfoSerializationSurrogate : ISerializationSurrogate
  46. {
  47. public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
  48. {
  49. info.SetType(typeof(MemberInfoReference));
  50. new MemberInfoReference((MemberInfo) obj).GetObjectData(info, context);
  51. }
  52. public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) =>
  53. throw new NotSupportedException();
  54. }
  55. [Serializable]
  56. private sealed class MemberInfoReference : IObjectReference, ISerializable
  57. {
  58. private readonly System.Type _declaringType;
  59. private readonly string _name;
  60. private readonly MemberTypes _memberType;
  61. private readonly BindingFlags _bindingFlags;
  62. private readonly System.Type[] _genericArguments;
  63. private readonly System.Type[] _parameterTypes;
  64. public MemberInfoReference(MemberInfo member)
  65. {
  66. if (member == null)
  67. throw new ArgumentNullException(nameof(member));
  68. _declaringType = member.DeclaringType;
  69. _name = member.Name;
  70. _memberType = member.MemberType;
  71. _bindingFlags = GetBindingFlags(member);
  72. if (member is MethodBase method)
  73. {
  74. _genericArguments = method.IsGenericMethod ? method.GetGenericArguments() : System.Type.EmptyTypes;
  75. _parameterTypes = method.GetParameters().ToArray(p => p.ParameterType);
  76. }
  77. }
  78. private MemberInfoReference(SerializationInfo info, StreamingContext context)
  79. {
  80. _declaringType = info.GetValue<System.Type>("DeclaringType");
  81. _name = info.GetString("Name");
  82. _memberType = info.GetValue<MemberTypes>("MemberType");
  83. _bindingFlags = info.GetValue<BindingFlags>("BindingFlags");
  84. _genericArguments = info.GetValueArray<System.Type>("GenericArguments");
  85. _parameterTypes = info.GetValueArray<System.Type>("ParameterTypes");
  86. }
  87. public void GetObjectData(SerializationInfo info, StreamingContext context)
  88. {
  89. info.AddValue("DeclaringType", _declaringType);
  90. info.AddValue("Name", _name);
  91. info.AddValue("MemberType", _memberType);
  92. info.AddValue("BindingFlags", _bindingFlags);
  93. info.AddValueArray("GenericArguments", _genericArguments);
  94. info.AddValueArray("ParameterTypes", _parameterTypes);
  95. }
  96. public object GetRealObject(StreamingContext context)
  97. {
  98. var members = _declaringType.GetMember(_name, _memberType, _bindingFlags | BindingFlags.DeclaredOnly);
  99. if (members.Length == 0) throw new MissingMemberException(_declaringType.FullName, _name);
  100. if (_memberType == MemberTypes.Method || _memberType == MemberTypes.Constructor)
  101. {
  102. try
  103. {
  104. return members.Cast<MethodBase>().First(MatchMethodSignature);
  105. }
  106. catch (InvalidOperationException)
  107. {
  108. throw new MissingMethodException(_declaringType.FullName, _name);
  109. }
  110. }
  111. if (members.Length > 1)
  112. throw new AmbiguousMatchException($"Found multiple \"{_name}\" in \"{_declaringType}\".");
  113. return members[0];
  114. }
  115. private bool MatchMethodSignature(MethodBase method)
  116. {
  117. var gpa = method.IsGenericMethod ? method.GetGenericArguments() : System.Type.EmptyTypes;
  118. if (gpa.Length != _genericArguments.Length) return false;
  119. var pa = method.GetParameters();
  120. if (pa.Length != _parameterTypes.Length) return false;
  121. if (gpa.Length > 0)
  122. {
  123. var genericMethod = ((MethodInfo) method).MakeGenericMethod(_genericArguments);
  124. pa = genericMethod.GetParameters();
  125. }
  126. for (int i = 0; i < pa.Length; i++)
  127. {
  128. if (pa[i].ParameterType != _parameterTypes[i])
  129. {
  130. return false;
  131. }
  132. }
  133. return true;
  134. }
  135. private static BindingFlags GetBindingFlags(MemberInfo member)
  136. {
  137. if (member == null) throw new ArgumentNullException(nameof(member));
  138. var bindingFlags = BindingFlags.Default;
  139. switch (member)
  140. {
  141. case MethodBase method:
  142. bindingFlags |= method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
  143. bindingFlags |= method.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
  144. break;
  145. case FieldInfo field:
  146. bindingFlags |= field.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
  147. bindingFlags |= field.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
  148. break;
  149. default:
  150. return BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
  151. }
  152. return bindingFlags;
  153. }
  154. }
  155. private sealed class DelegateSerializationSurrogate : ISerializationSurrogate
  156. {
  157. public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
  158. {
  159. info.SetType(typeof(DelegateReference));
  160. new DelegateReference((Delegate) obj).GetObjectData(info, context);
  161. }
  162. public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) =>
  163. throw new NotSupportedException();
  164. }
  165. [Serializable]
  166. private sealed class DelegateReference : IObjectReference, ISerializable
  167. {
  168. private readonly System.Type[] _types;
  169. private readonly MethodInfo[] _methods;
  170. private readonly object[] _targets;
  171. public DelegateReference(Delegate @delegate)
  172. {
  173. if (@delegate == null)
  174. throw new ArgumentNullException(nameof(@delegate));
  175. var invocations = @delegate.GetInvocationList();
  176. _types = new System.Type[invocations.Length];
  177. _methods = new MethodInfo[invocations.Length];
  178. _targets = new object[invocations.Length];
  179. for (var i = 0; i < invocations.Length; i++)
  180. {
  181. var invocation = invocations[i];
  182. _types[i] = invocation.GetType();
  183. _methods[i] = invocation.Method;
  184. _targets[i] = invocation.Target;
  185. }
  186. }
  187. private DelegateReference(SerializationInfo info, StreamingContext context)
  188. {
  189. _types = GetValueArray<System.Type>(info, "Types");
  190. _methods = info.GetValueArray<MethodInfo>("Methods");
  191. _targets = info.GetValueArray<object>("Targets");
  192. }
  193. public void GetObjectData(SerializationInfo info, StreamingContext context)
  194. {
  195. info.AddValueArray("Types", _types);
  196. info.AddValueArray("Methods", _methods);
  197. info.AddValueArray("Targets", _targets);
  198. }
  199. public object GetRealObject(StreamingContext context)
  200. {
  201. var delegates = new Delegate[_types.Length];
  202. for (var i = 0; i < delegates.Length; i++)
  203. delegates[i] = Delegate.CreateDelegate(_types[i], _targets[i], _methods[i]);
  204. return delegates.Length == 0
  205. ? delegates[0]
  206. : Delegate.Combine(delegates);
  207. }
  208. }
  209. public sealed class SurrogateSelector : System.Runtime.Serialization.SurrogateSelector
  210. {
  211. private static readonly bool SystemTypeIsNotSerializable = !typeof(System.Type).IsSerializable;
  212. private static readonly bool MemberInfoIsNotSerializable = !typeof(MemberInfo).IsSerializable;
  213. private static readonly bool DelegateIsNotSerializable = !GetDelegateIsSerializable();
  214. private static bool GetDelegateIsSerializable()
  215. {
  216. //In .NET Standard & .NET Core Delegate implements ISerializable,
  217. //but throws SerializationException
  218. if (!typeof(Delegate).IsSerializable)
  219. return false;
  220. try
  221. {
  222. System.Action a = () => { };
  223. a.GetObjectData(
  224. new SerializationInfo(typeof(Delegate), new FormatterConverter()),
  225. new StreamingContext(StreamingContextStates.All));
  226. return true;
  227. }
  228. catch (PlatformNotSupportedException)
  229. {
  230. return false;
  231. }
  232. catch (SerializationException)
  233. {
  234. return false;
  235. }
  236. }
  237. public override ISerializationSurrogate GetSurrogate(
  238. System.Type type,
  239. StreamingContext context,
  240. out ISurrogateSelector selector)
  241. {
  242. if (SystemTypeIsNotSerializable && typeof(System.Type).IsAssignableFrom(type))
  243. {
  244. selector = this;
  245. return new TypeSerializationSurrogate();
  246. }
  247. if (MemberInfoIsNotSerializable && typeof(MemberInfo).IsAssignableFrom(type))
  248. {
  249. selector = this;
  250. return new MemberInfoSerializationSurrogate();
  251. }
  252. if (DelegateIsNotSerializable && typeof(Delegate).IsAssignableFrom(type))
  253. {
  254. selector = this;
  255. return new DelegateSerializationSurrogate();
  256. }
  257. return base.GetSurrogate(type, context, out selector);
  258. }
  259. public override void AddSurrogate(System.Type type, StreamingContext context, ISerializationSurrogate surrogate) =>
  260. throw new NotSupportedException("This is a static SurrogateSelector");
  261. public override void RemoveSurrogate(System.Type type, StreamingContext context) =>
  262. throw new NotSupportedException("This is a static SurrogateSelector");
  263. }
  264. }
  265. }
  266. #endif