PageRenderTime 52ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs

https://gitlab.com/godotengine/godot
C# | 395 lines | 305 code | 89 blank | 1 comment | 69 complexity | 73fa8e7f7fd88012382a366a58903118 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Runtime.CompilerServices;
  7. namespace Godot
  8. {
  9. internal static class DelegateUtils
  10. {
  11. private enum TargetKind : uint
  12. {
  13. Static,
  14. GodotObject,
  15. CompilerGenerated
  16. }
  17. internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
  18. {
  19. if (@delegate is MulticastDelegate multicastDelegate)
  20. {
  21. bool someDelegatesSerialized = false;
  22. Delegate[] invocationList = multicastDelegate.GetInvocationList();
  23. if (invocationList.Length > 1)
  24. {
  25. var multiCastData = new Collections.Array();
  26. foreach (Delegate oneDelegate in invocationList)
  27. someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData);
  28. if (!someDelegatesSerialized)
  29. return false;
  30. serializedData.Add(multiCastData);
  31. return true;
  32. }
  33. }
  34. if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
  35. {
  36. serializedData.Add(buffer);
  37. return true;
  38. }
  39. return false;
  40. }
  41. private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
  42. {
  43. buffer = null;
  44. object target = @delegate.Target;
  45. switch (target)
  46. {
  47. case null:
  48. {
  49. using (var stream = new MemoryStream())
  50. using (var writer = new BinaryWriter(stream))
  51. {
  52. writer.Write((ulong)TargetKind.Static);
  53. SerializeType(writer, @delegate.GetType());
  54. if (!TrySerializeMethodInfo(writer, @delegate.Method))
  55. return false;
  56. buffer = stream.ToArray();
  57. return true;
  58. }
  59. }
  60. case Godot.Object godotObject:
  61. {
  62. using (var stream = new MemoryStream())
  63. using (var writer = new BinaryWriter(stream))
  64. {
  65. writer.Write((ulong)TargetKind.GodotObject);
  66. writer.Write((ulong)godotObject.GetInstanceId());
  67. SerializeType(writer, @delegate.GetType());
  68. if (!TrySerializeMethodInfo(writer, @delegate.Method))
  69. return false;
  70. buffer = stream.ToArray();
  71. return true;
  72. }
  73. }
  74. default:
  75. {
  76. Type targetType = target.GetType();
  77. if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
  78. {
  79. // Compiler generated. Probably a closure. Try to serialize it.
  80. using (var stream = new MemoryStream())
  81. using (var writer = new BinaryWriter(stream))
  82. {
  83. writer.Write((ulong)TargetKind.CompilerGenerated);
  84. SerializeType(writer, targetType);
  85. SerializeType(writer, @delegate.GetType());
  86. if (!TrySerializeMethodInfo(writer, @delegate.Method))
  87. return false;
  88. FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public);
  89. writer.Write(fields.Length);
  90. foreach (FieldInfo field in fields)
  91. {
  92. Type fieldType = field.GetType();
  93. Variant.Type variantType = GD.TypeToVariantType(fieldType);
  94. if (variantType == Variant.Type.Nil)
  95. return false;
  96. writer.Write(field.Name);
  97. byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
  98. writer.Write(valueBuffer.Length);
  99. writer.Write(valueBuffer);
  100. }
  101. buffer = stream.ToArray();
  102. return true;
  103. }
  104. }
  105. return false;
  106. }
  107. }
  108. }
  109. private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
  110. {
  111. if (methodInfo == null)
  112. return false;
  113. SerializeType(writer, methodInfo.DeclaringType);
  114. writer.Write(methodInfo.Name);
  115. int flags = 0;
  116. if (methodInfo.IsPublic)
  117. flags |= (int)BindingFlags.Public;
  118. else
  119. flags |= (int)BindingFlags.NonPublic;
  120. if (methodInfo.IsStatic)
  121. flags |= (int)BindingFlags.Static;
  122. else
  123. flags |= (int)BindingFlags.Instance;
  124. writer.Write(flags);
  125. Type returnType = methodInfo.ReturnType;
  126. bool hasReturn = methodInfo.ReturnType != typeof(void);
  127. writer.Write(hasReturn);
  128. if (hasReturn)
  129. SerializeType(writer, returnType);
  130. ParameterInfo[] parameters = methodInfo.GetParameters();
  131. writer.Write(parameters.Length);
  132. if (parameters.Length > 0)
  133. {
  134. for (int i = 0; i < parameters.Length; i++)
  135. SerializeType(writer, parameters[i].ParameterType);
  136. }
  137. return true;
  138. }
  139. private static void SerializeType(BinaryWriter writer, Type type)
  140. {
  141. if (type == null)
  142. {
  143. int genericArgumentsCount = -1;
  144. writer.Write(genericArgumentsCount);
  145. }
  146. else if (type.IsGenericType)
  147. {
  148. Type genericTypeDef = type.GetGenericTypeDefinition();
  149. Type[] genericArgs = type.GetGenericArguments();
  150. int genericArgumentsCount = genericArgs.Length;
  151. writer.Write(genericArgumentsCount);
  152. string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
  153. Debug.Assert(assemblyQualifiedName != null);
  154. writer.Write(assemblyQualifiedName);
  155. for (int i = 0; i < genericArgs.Length; i++)
  156. SerializeType(writer, genericArgs[i]);
  157. }
  158. else
  159. {
  160. int genericArgumentsCount = 0;
  161. writer.Write(genericArgumentsCount);
  162. string assemblyQualifiedName = type.AssemblyQualifiedName;
  163. Debug.Assert(assemblyQualifiedName != null);
  164. writer.Write(assemblyQualifiedName);
  165. }
  166. }
  167. private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
  168. {
  169. if (serializedData.Count == 1)
  170. {
  171. object elem = serializedData[0];
  172. if (elem is Collections.Array multiCastData)
  173. return TryDeserializeDelegate(multiCastData, out @delegate);
  174. return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
  175. }
  176. @delegate = null;
  177. var delegates = new List<Delegate>(serializedData.Count);
  178. foreach (object elem in serializedData)
  179. {
  180. if (elem is Collections.Array multiCastData)
  181. {
  182. if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
  183. delegates.Add(oneDelegate);
  184. }
  185. else
  186. {
  187. if (TryDeserializeSingleDelegate((byte[])elem, out Delegate oneDelegate))
  188. delegates.Add(oneDelegate);
  189. }
  190. }
  191. if (delegates.Count <= 0)
  192. return false;
  193. @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
  194. return true;
  195. }
  196. private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
  197. {
  198. @delegate = null;
  199. using (var stream = new MemoryStream(buffer, writable: false))
  200. using (var reader = new BinaryReader(stream))
  201. {
  202. var targetKind = (TargetKind)reader.ReadUInt64();
  203. switch (targetKind)
  204. {
  205. case TargetKind.Static:
  206. {
  207. Type delegateType = DeserializeType(reader);
  208. if (delegateType == null)
  209. return false;
  210. if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
  211. return false;
  212. @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
  213. return true;
  214. }
  215. case TargetKind.GodotObject:
  216. {
  217. ulong objectId = reader.ReadUInt64();
  218. Godot.Object godotObject = GD.InstanceFromId(objectId);
  219. if (godotObject == null)
  220. return false;
  221. Type delegateType = DeserializeType(reader);
  222. if (delegateType == null)
  223. return false;
  224. if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
  225. return false;
  226. @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
  227. return true;
  228. }
  229. case TargetKind.CompilerGenerated:
  230. {
  231. Type targetType = DeserializeType(reader);
  232. if (targetType == null)
  233. return false;
  234. Type delegateType = DeserializeType(reader);
  235. if (delegateType == null)
  236. return false;
  237. if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
  238. return false;
  239. int fieldCount = reader.ReadInt32();
  240. object recreatedTarget = Activator.CreateInstance(targetType);
  241. for (int i = 0; i < fieldCount; i++)
  242. {
  243. string name = reader.ReadString();
  244. int valueBufferLength = reader.ReadInt32();
  245. byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
  246. FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
  247. fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
  248. }
  249. @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
  250. return true;
  251. }
  252. default:
  253. return false;
  254. }
  255. }
  256. }
  257. private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
  258. {
  259. methodInfo = null;
  260. Type declaringType = DeserializeType(reader);
  261. string methodName = reader.ReadString();
  262. int flags = reader.ReadInt32();
  263. bool hasReturn = reader.ReadBoolean();
  264. Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
  265. int parametersCount = reader.ReadInt32();
  266. if (parametersCount > 0)
  267. {
  268. var parameterTypes = new Type[parametersCount];
  269. for (int i = 0; i < parametersCount; i++)
  270. {
  271. Type parameterType = DeserializeType(reader);
  272. if (parameterType == null)
  273. return false;
  274. parameterTypes[i] = parameterType;
  275. }
  276. methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
  277. return methodInfo != null && methodInfo.ReturnType == returnType;
  278. }
  279. methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags);
  280. return methodInfo != null && methodInfo.ReturnType == returnType;
  281. }
  282. private static Type DeserializeType(BinaryReader reader)
  283. {
  284. int genericArgumentsCount = reader.ReadInt32();
  285. if (genericArgumentsCount == -1)
  286. return null;
  287. string assemblyQualifiedName = reader.ReadString();
  288. var type = Type.GetType(assemblyQualifiedName);
  289. if (type == null)
  290. return null; // Type not found
  291. if (genericArgumentsCount != 0)
  292. {
  293. var genericArgumentTypes = new Type[genericArgumentsCount];
  294. for (int i = 0; i < genericArgumentsCount; i++)
  295. {
  296. Type genericArgumentType = DeserializeType(reader);
  297. if (genericArgumentType == null)
  298. return null;
  299. genericArgumentTypes[i] = genericArgumentType;
  300. }
  301. type = type.MakeGenericType(genericArgumentTypes);
  302. }
  303. return type;
  304. }
  305. }
  306. }