/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
C# | 395 lines | 305 code | 89 blank | 1 comment | 69 complexity | 73fa8e7f7fd88012382a366a58903118 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- namespace Godot
- {
- internal static class DelegateUtils
- {
- private enum TargetKind : uint
- {
- Static,
- GodotObject,
- CompilerGenerated
- }
- internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
- {
- if (@delegate is MulticastDelegate multicastDelegate)
- {
- bool someDelegatesSerialized = false;
- Delegate[] invocationList = multicastDelegate.GetInvocationList();
- if (invocationList.Length > 1)
- {
- var multiCastData = new Collections.Array();
- foreach (Delegate oneDelegate in invocationList)
- someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData);
- if (!someDelegatesSerialized)
- return false;
- serializedData.Add(multiCastData);
- return true;
- }
- }
- if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
- {
- serializedData.Add(buffer);
- return true;
- }
- return false;
- }
- private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
- {
- buffer = null;
- object target = @delegate.Target;
- switch (target)
- {
- case null:
- {
- using (var stream = new MemoryStream())
- using (var writer = new BinaryWriter(stream))
- {
- writer.Write((ulong)TargetKind.Static);
- SerializeType(writer, @delegate.GetType());
- if (!TrySerializeMethodInfo(writer, @delegate.Method))
- return false;
- buffer = stream.ToArray();
- return true;
- }
- }
- case Godot.Object godotObject:
- {
- using (var stream = new MemoryStream())
- using (var writer = new BinaryWriter(stream))
- {
- writer.Write((ulong)TargetKind.GodotObject);
- writer.Write((ulong)godotObject.GetInstanceId());
- SerializeType(writer, @delegate.GetType());
- if (!TrySerializeMethodInfo(writer, @delegate.Method))
- return false;
- buffer = stream.ToArray();
- return true;
- }
- }
- default:
- {
- Type targetType = target.GetType();
- if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
- {
- // Compiler generated. Probably a closure. Try to serialize it.
- using (var stream = new MemoryStream())
- using (var writer = new BinaryWriter(stream))
- {
- writer.Write((ulong)TargetKind.CompilerGenerated);
- SerializeType(writer, targetType);
- SerializeType(writer, @delegate.GetType());
- if (!TrySerializeMethodInfo(writer, @delegate.Method))
- return false;
- FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public);
- writer.Write(fields.Length);
- foreach (FieldInfo field in fields)
- {
- Type fieldType = field.GetType();
- Variant.Type variantType = GD.TypeToVariantType(fieldType);
- if (variantType == Variant.Type.Nil)
- return false;
- writer.Write(field.Name);
- byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
- writer.Write(valueBuffer.Length);
- writer.Write(valueBuffer);
- }
- buffer = stream.ToArray();
- return true;
- }
- }
- return false;
- }
- }
- }
- private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
- {
- if (methodInfo == null)
- return false;
- SerializeType(writer, methodInfo.DeclaringType);
- writer.Write(methodInfo.Name);
- int flags = 0;
- if (methodInfo.IsPublic)
- flags |= (int)BindingFlags.Public;
- else
- flags |= (int)BindingFlags.NonPublic;
- if (methodInfo.IsStatic)
- flags |= (int)BindingFlags.Static;
- else
- flags |= (int)BindingFlags.Instance;
- writer.Write(flags);
- Type returnType = methodInfo.ReturnType;
- bool hasReturn = methodInfo.ReturnType != typeof(void);
- writer.Write(hasReturn);
- if (hasReturn)
- SerializeType(writer, returnType);
- ParameterInfo[] parameters = methodInfo.GetParameters();
- writer.Write(parameters.Length);
- if (parameters.Length > 0)
- {
- for (int i = 0; i < parameters.Length; i++)
- SerializeType(writer, parameters[i].ParameterType);
- }
- return true;
- }
- private static void SerializeType(BinaryWriter writer, Type type)
- {
- if (type == null)
- {
- int genericArgumentsCount = -1;
- writer.Write(genericArgumentsCount);
- }
- else if (type.IsGenericType)
- {
- Type genericTypeDef = type.GetGenericTypeDefinition();
- Type[] genericArgs = type.GetGenericArguments();
- int genericArgumentsCount = genericArgs.Length;
- writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
- for (int i = 0; i < genericArgs.Length; i++)
- SerializeType(writer, genericArgs[i]);
- }
- else
- {
- int genericArgumentsCount = 0;
- writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = type.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
- }
- }
- private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
- {
- if (serializedData.Count == 1)
- {
- object elem = serializedData[0];
- if (elem is Collections.Array multiCastData)
- return TryDeserializeDelegate(multiCastData, out @delegate);
- return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
- }
- @delegate = null;
- var delegates = new List<Delegate>(serializedData.Count);
- foreach (object elem in serializedData)
- {
- if (elem is Collections.Array multiCastData)
- {
- if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
- delegates.Add(oneDelegate);
- }
- else
- {
- if (TryDeserializeSingleDelegate((byte[])elem, out Delegate oneDelegate))
- delegates.Add(oneDelegate);
- }
- }
- if (delegates.Count <= 0)
- return false;
- @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
- return true;
- }
- private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
- {
- @delegate = null;
- using (var stream = new MemoryStream(buffer, writable: false))
- using (var reader = new BinaryReader(stream))
- {
- var targetKind = (TargetKind)reader.ReadUInt64();
- switch (targetKind)
- {
- case TargetKind.Static:
- {
- Type delegateType = DeserializeType(reader);
- if (delegateType == null)
- return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
- return false;
- @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
- return true;
- }
- case TargetKind.GodotObject:
- {
- ulong objectId = reader.ReadUInt64();
- Godot.Object godotObject = GD.InstanceFromId(objectId);
- if (godotObject == null)
- return false;
- Type delegateType = DeserializeType(reader);
- if (delegateType == null)
- return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
- return false;
- @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
- return true;
- }
- case TargetKind.CompilerGenerated:
- {
- Type targetType = DeserializeType(reader);
- if (targetType == null)
- return false;
- Type delegateType = DeserializeType(reader);
- if (delegateType == null)
- return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
- return false;
- int fieldCount = reader.ReadInt32();
- object recreatedTarget = Activator.CreateInstance(targetType);
- for (int i = 0; i < fieldCount; i++)
- {
- string name = reader.ReadString();
- int valueBufferLength = reader.ReadInt32();
- byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
- FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
- fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
- }
- @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
- return true;
- }
- default:
- return false;
- }
- }
- }
- private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
- {
- methodInfo = null;
- Type declaringType = DeserializeType(reader);
- string methodName = reader.ReadString();
- int flags = reader.ReadInt32();
- bool hasReturn = reader.ReadBoolean();
- Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
- int parametersCount = reader.ReadInt32();
- if (parametersCount > 0)
- {
- var parameterTypes = new Type[parametersCount];
- for (int i = 0; i < parametersCount; i++)
- {
- Type parameterType = DeserializeType(reader);
- if (parameterType == null)
- return false;
- parameterTypes[i] = parameterType;
- }
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
- return methodInfo != null && methodInfo.ReturnType == returnType;
- }
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags);
- return methodInfo != null && methodInfo.ReturnType == returnType;
- }
- private static Type DeserializeType(BinaryReader reader)
- {
- int genericArgumentsCount = reader.ReadInt32();
- if (genericArgumentsCount == -1)
- return null;
- string assemblyQualifiedName = reader.ReadString();
- var type = Type.GetType(assemblyQualifiedName);
- if (type == null)
- return null; // Type not found
- if (genericArgumentsCount != 0)
- {
- var genericArgumentTypes = new Type[genericArgumentsCount];
- for (int i = 0; i < genericArgumentsCount; i++)
- {
- Type genericArgumentType = DeserializeType(reader);
- if (genericArgumentType == null)
- return null;
- genericArgumentTypes[i] = genericArgumentType;
- }
- type = type.MakeGenericType(genericArgumentTypes);
- }
- return type;
- }
- }
- }