using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using JetBrains.Collections;
using JetBrains.Collections.Viewable;
using JetBrains.Diagnostics;
using JetBrains.Rd.Base;
using JetBrains.Rd.Impl;
using JetBrains.Serialization;
using JetBrains.Util;
using JetBrains.Util.Util;
using static System.String;
namespace JetBrains.Rd.Reflection;
///
/// Creates and provides access to Reflection-generated serializers for Rd, thread safe
///
public class ReflectionSerializers : ISerializers, ISerializersSource
{
///
/// Collection static serializers (serializers is not possible here! Only instance serializer can be serializers)
///
private readonly Dictionary myStaticSerializers = new();
///
/// Collection of specific serializers serializers and user-registred custom serializers.
/// User registred serializers should be added before activating any other serializers serializer to guarantee
/// consistency of serializers across all Rd objects.
///
/// Techincally, this restriction can be lifted to lazy initialization. It only exists to reduce the
/// amount of races in consumers.
///
private readonly Dictionary myInstanceSerializers = new();
///
/// A flag to enforce consistency of serializers. New specific poly serializer can't be registered after first query
/// of serializers serializer from outer world.
///
private bool myPolySerializersSealed = false;
private readonly ITypesCatalog myCatalog;
private readonly IScalarSerializers myScalars;
private readonly object myLock = new object();
///
/// current serialization stack.
///
/// used to provide diagnostics about circular dependencies only.
///
private readonly Queue myCurrentSerializersChain = new Queue();
public IScalarSerializers Scalars => myScalars;
///
/// An extension point to override lazy creation of serializers instance serializers.
/// You add your custom serializer for generic types via this extension point.
/// You can also use method ahead of time when lazy initialization isn't necessary.
///
public ISignal BeforeCreation { get; } = new Signal();
public ReflectionSerializers(ITypesCatalog typeCatalog, IScalarSerializers? scalars = null, Predicate? blackListChecker = null, bool withExtensions = true)
{
myCatalog = typeCatalog;
myScalars = scalars ?? new ScalarSerializer(typeCatalog, blackListChecker);
Serializers.RegisterFrameworkMarshallers(this);
if (withExtensions)
{
ScalarCollectionExtension.AttachCollectionSerializers(this);
}
}
public SerializerPair GetOrRegisterSerializerPair(Type type, bool instance = false)
{
lock (myLock)
{
var implementingType = ReflectionSerializerVerifier.GetImplementingType(type.GetTypeInfo()).GetTypeInfo();
var isRdType = implementingType != type;
if (!isRdType && instance && SerializerReflectionUtil.CanBePolymorphic(type))
{
myCatalog.AddType(type);
if (myInstanceSerializers.TryGetValue(type, out var pair))
return pair;
BeforeCreation.Fire(type);
if (myInstanceSerializers.TryGetValue(type, out pair))
return pair;
return GetPolymorphic(type);
}
if (!myStaticSerializers.TryGetValue(type, out var serializerPair))
{
using var info = new FirstChanceExceptionInterceptor.ThreadLocalDebugInfo(type);
myCatalog.AddType(type);
BeforeCreation.Fire(type);
if (myStaticSerializers.TryGetValue(type, out serializerPair))
return serializerPair;
if (Mode.IsAssertion) myCurrentSerializersChain.Enqueue(type);
try
{
myStaticSerializers.Add(type, null!);
if (isRdType)
{
var builtIn = BuiltInSerializers.TryGet(implementingType, t => GetOrRegisterSerializerPair(t, true));
Assertion.AssertNotNull(builtIn, "Unable to get built in serializer for type {0}, thought it should be implemented for Rd-types.", type);
var pair = SerializerReflectionUtil.ConvertPair(builtIn, type);
myStaticSerializers[type] = pair;
}
else if (ReflectionSerializerVerifier.IsScalar(type))
{
myStaticSerializers[type] = CreateScalar(type, instance);
}
else if (BuiltInSerializers.Has(type.GetTypeInfo()))
{
var builtIn = BuiltInSerializers.TryGet(
type.GetTypeInfo(),
t => GetOrRegisterSerializerPair(t, true));
Assertion.AssertNotNull(builtIn, "Unable to get built in serializer for type {0}, thought API detect the presense of it. Probably it was only partially implemented",
type);
myStaticSerializers[type] = builtIn;
}
else
{
ReflectionUtil.InvokeGenericThis(this, nameof(RegisterModelSerializer), type);
}
}
finally
{
if (Mode.IsAssertion) myCurrentSerializersChain.Dequeue();
}
if (!myStaticSerializers.TryGetValue(type, out serializerPair))
{
throw new KeyNotFoundException($"Unable to register type {type.ToString(true)}: serializer can't be found");
}
}
if (Mode.IsAssertion && serializerPair == null)
Assertion.Fail(
$"Unable to create serializer for {type.ToString(true)}: circular dependency detected: {Join(" -> ", myCurrentSerializersChain.Select(t => t.ToString(true)).ToArray())}");
else
Assertion.AssertNotNull(serializerPair,
$"Unable to register type: {type.ToString(true)}, undetected circular dependency. Enable Mode.IsAssertion to get the cycle.");
return serializerPair;
}
}
private SerializerPair CreateScalar(Type serializerType, bool instanceSerializer)
{
if (instanceSerializer && SerializerReflectionUtil.CanBePolymorphic(serializerType))
{
return GetPolymorphic(serializerType);
}
var pair = myScalars.CreateSerializer(serializerType, this);
if (pair == null)
Assertion.Fail($"Unable to Create serializer for scalar type {serializerType.ToString(true)}");
else if (Mode.IsAssertion)
Assertion.Assert(!pair.IsPolymorphic, "Polymorphic serializer can't be stored in staticSerializers");
return pair;
}
///
/// Register serializers for either or
///
private void RegisterModelSerializer()
{
if (Mode.IsAssertion) Assertion.Assert(!ReflectionSerializerVerifier.IsScalar(typeof(T)), "Type {0} should be either RdModel or RdExt.", typeof(T));
var typeInfo = typeof(T).GetTypeInfo();
ReflectionSerializerVerifier.AssertRoot(typeInfo);
var isScalar = ReflectionSerializerVerifier.IsScalar(typeInfo);
bool allowNullable = ReflectionSerializerVerifier.HasRdModelAttribute(typeInfo) || (isScalar && ReflectionSerializerVerifier.CanBeNull(typeInfo));
/* var builtInSerializer = TryGetBuiltInSerializer(typeInfo);
if (builtInSerializer != null)
{
myStaticSerializers[typeof(T)] = builtInSerializer;
return;
}*/
var memberInfos = SerializerReflectionUtil.GetSerializableFields(typeInfo);
var memberSetters = memberInfos.Select(ReflectionUtil.GetSetter).ToArray();
var memberGetters = memberInfos.Select(ReflectionUtil.GetGetter).ToArray();
// todo: consider using IL emit
var memberDeserializers = new CtxReadDelegate