using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices.ComTypes; using JetBrains.Diagnostics; using JetBrains.Rd.Base; using JetBrains.Serialization; using JetBrains.Util.Util; namespace JetBrains.Rd.Reflection { public class SerializerReflectionUtil { /// /// Get lists of members which take part in object serialization. /// Can be used for RdExt, RdModel and any RdScalar. /// internal static FieldInfo[] GetSerializableFields(TypeInfo typeInfo) { /* var rpcInterface = GetRpcInterface(); if (rpcInterface != null) { var rpcInterfaceMap = typeInfo.GetInterfaceMap(rpcInterface); //members = rpcInterfaceMap.TargetMethods; } */ Type baseType; if (ReflectionSerializerVerifier.HasRdExtAttribute(typeInfo)) baseType = typeof(RdExtReflectionBindableBase); else if (ReflectionSerializerVerifier.HasRdModelAttribute(typeInfo)) baseType = typeof(RdReflectionBindableBase); else baseType = typeof(RdBindableBase); bool isRdExtImpl = baseType == typeof(RdExtReflectionBindableBase) && !typeInfo.GetInterfaces().Contains(typeof(IProxyTypeMarker)); bool isRdRpcInterface = typeInfo.IsInterface; // can be specified in RdExt // && typeInfo.GetCustomAttribute() != null; var fields = GetFields(typeInfo, baseType); var list = new List(); foreach (var mi in fields) { if (typeof(RdExtReflectionBindableBase).IsAssignableFrom(mi.FieldType)) continue; if ( mi.MemberType == MemberTypes.Field && (mi.DeclaringType != null && !mi.DeclaringType.GetTypeInfo().IsAssignableFrom(baseType)) && mi.GetCustomAttribute() == null && // arbitrary data is allowed in RdExt implementations since they don't have to be serializable !(isRdExtImpl && ReflectionSerializerVerifier.IsScalar(ReflectionSerializerVerifier.GetImplementingType(mi.FieldType.GetTypeInfo()))) ) { list.Add(mi); } else if (isRdRpcInterface) { throw new Exception($"Invalid member in RdRpc interface: {typeInfo.ToString(true)}.{mi.Name}"); } } return list.ToArray(); } private static IEnumerable GetFields(Type type, Type baseType) { foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) yield return field; // private fields only being returned for the current type while ((type = type.BaseType) != baseType && type != null) { // but protected fields are returned in first step foreach (var baseField in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) if (baseField.IsPrivate) yield return baseField; } } internal static SerializerPair ConvertPair(SerializerPair serializers, Type desiredType) { return (SerializerPair)ourConvertSerializerPair.MakeGenericMethod(serializers.Writer.GetType().GetGenericArguments()[0], desiredType).Invoke(null, new object[] { serializers }); } private static readonly MethodInfo ourConvertSerializerPair = typeof(SerializerReflectionUtil).GetTypeInfo().GetMethod(nameof(ConvertPairGeneric), BindingFlags.Static | BindingFlags.NonPublic)!; private static SerializerPair ConvertPairGeneric(SerializerPair serializers) { if (typeof(TIn) == typeof(TOut)) return serializers; var read = serializers.GetReader(); var write = serializers.GetWriter(); if (typeof(TIn).IsClass && typeof(TOut).IsClass) { if (typeof(TOut).IsAssignableFrom(typeof(TIn))) return new SerializerPair(read, CtxWriteTypedToObject(write)); if (typeof(TIn).IsAssignableFrom(typeof(TOut))) return new SerializerPair(CtxReadTypedToObject(read), write); } return new SerializerPair(CtxReadTypedToObject(read), CtxWriteTypedToObject(write)); } internal static CtxReadDelegate ConvertReader(object reader) { if (reader is CtxReadDelegate objReader) return objReader; var genericTypedRead = ourConvertTypedCtxRead.MakeGenericMethod(reader.GetType().GetGenericArguments()[0], typeof(object)); var result = genericTypedRead.Invoke(null, new[] { reader }); return (CtxReadDelegate)result; } internal static CtxWriteDelegate ConvertWriter(object writer) { if (writer is CtxWriteDelegate objWriter) return objWriter; return (CtxWriteDelegate)ourConvertTypedCtxWrite.MakeGenericMethod(writer.GetType().GetGenericArguments()[0], typeof(TOut)).Invoke(null, new[] { writer }); } private static readonly MethodInfo ourConvertTypedCtxRead = typeof(SerializerReflectionUtil).GetTypeInfo().GetMethod(nameof(CtxReadTypedToObject), BindingFlags.Static | BindingFlags.NonPublic)!; private static CtxReadDelegate CtxReadTypedToObject(CtxReadDelegate typedDelegate) { return (ctx, unsafeReader) => (TOut) (object) typedDelegate(ctx, unsafeReader)!; } private static readonly MethodInfo ourConvertTypedCtxWrite = typeof(SerializerReflectionUtil).GetTypeInfo().GetMethod(nameof(CtxWriteTypedToObject), BindingFlags.Static | BindingFlags.NonPublic)!; private static CtxWriteDelegate CtxWriteTypedToObject(CtxWriteDelegate typedDelegate) { return (ctx, unsafeWriter, value) => typedDelegate(ctx, unsafeWriter, (TIn) (object) value!) ; } public static bool CanBePolymorphic(Type type) { /*if (IsList(type) || IsDictionary(type)) return false; */ return (type.IsClass && !type.IsSealed) || type.IsInterface; } } }