From 851ef043e5a8ecd93c519b5b9fa88c58f6a277d1 Mon Sep 17 00:00:00 2001 From: qx Date: Tue, 4 May 2021 19:41:35 +0300 Subject: [PATCH 1/3] ReflectionRdActivator: allow to instantiate reactive types. Currently we allow sealed classes assignable from IRdBindable. --- .../RdFramework.Reflection/ReflectionRdActivator.cs | 4 +++- .../ReflectionSerializerVerifier.cs | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs index 9dbc01a97..7a2999aea 100644 --- a/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs +++ b/rd-net/RdFramework.Reflection/ReflectionRdActivator.cs @@ -143,7 +143,9 @@ private object ActivateRd(Type type) #endif var typeInfo = type.GetTypeInfo(); - ReflectionSerializerVerifier.AssertEitherExtModelAttribute(typeInfo); + if (!ReflectionSerializerVerifier.IsSealedClassAssignableFromIRdBindable(typeInfo)) + ReflectionSerializerVerifier.AssertEitherExtModelAttribute(typeInfo); + var implementingType = ReflectionSerializerVerifier.GetImplementingType(typeInfo); Assertion.Assert(typeof(RdBindableBase).GetTypeInfo().IsAssignableFrom(implementingType), $"Unable to activate {type.FullName}: type should be {nameof(RdBindableBase)}"); diff --git a/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs b/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs index 250443190..b55e71af1 100644 --- a/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs +++ b/rd-net/RdFramework.Reflection/ReflectionSerializerVerifier.cs @@ -187,7 +187,7 @@ private static bool IsMemberType(TypeInfo typeInfo) genericDefinition == typeof(RdMap<,>) || // TResponse can be LiveModel (genericDefinition == typeof(RdCall<,>) && IsScalar(arguments[0]) /*&& IsScalar(arguments[1])*/) || // Custom classes support - (typeInfo.IsClass && typeInfo.IsSealed && typeof(IRdBindable).IsAssignableFrom(typeInfo)); + (IsSealedClassAssignableFromIRdBindable(typeInfo)); } if (IsScalar(typeInfo)) @@ -201,9 +201,18 @@ private static bool IsMemberType(TypeInfo typeInfo) if (hasRdModel) return true; + // Custom classes support + if (IsSealedClassAssignableFromIRdBindable(typeInfo)) + return true; + return false; } + public static bool IsSealedClassAssignableFromIRdBindable(TypeInfo typeInfo) + { + return typeInfo.IsClass && typeInfo.IsSealed && typeof(IRdBindable).IsAssignableFrom(typeInfo); + } + public static bool IsScalar(Type type) { return !typeof(IRdBindable).IsAssignableFrom(type); @@ -212,7 +221,7 @@ public static bool IsScalar(Type type) public static void AssertEitherExtModelAttribute(TypeInfo type) { /*Assertion.Assert((HasRdExtAttribute(type) || HasRdModelAttribute(type)), $"Invalid RdModel: expected to have either {nameof(RdModelAttribute)} or {nameof(RdExtAttribute)} ({type.ToString(true)}).");*/ - Assertion.Assert(HasRdExtAttribute(type) ^ HasRdModelAttribute(type), $"Invalid RdModel {type.ToString(true)}: expected to have only one of {nameof(RdModelAttribute)} or {nameof(RdExtAttribute)}."); + Assertion.Assert(HasRdExtAttribute(type) ^ HasRdModelAttribute(type), $"Invalid RdModel {type.ToString(true)}: expected to have only one of {nameof(RdModelAttribute)} or {nameof(RdExtAttribute)} or be a sealed class assignable from {nameof(IRdBindable)}."); } public static void AssertRoot(TypeInfo type) From 0497d1cab7eace51be2fc4ea511f45ce9f28ea92 Mon Sep 17 00:00:00 2001 From: qx Date: Tue, 4 May 2021 19:42:07 +0300 Subject: [PATCH 2/3] RdOuterLifetime: abstraction to send OuterLifetime over the protocol --- .../RdFramework.Reflection/RdOuterLifetime.cs | 71 +++++++++++++++++++ .../Test.RdFramework/RdOuterLifetimeTest.cs | 51 +++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 rd-net/RdFramework.Reflection/RdOuterLifetime.cs create mode 100644 rd-net/Test.RdFramework/RdOuterLifetimeTest.cs diff --git a/rd-net/RdFramework.Reflection/RdOuterLifetime.cs b/rd-net/RdFramework.Reflection/RdOuterLifetime.cs new file mode 100644 index 000000000..b4e25fe2e --- /dev/null +++ b/rd-net/RdFramework.Reflection/RdOuterLifetime.cs @@ -0,0 +1,71 @@ +using System; +using JetBrains.Annotations; +using JetBrains.Lifetimes; +using JetBrains.Rd.Base; +using JetBrains.Rd.Impl; +using JetBrains.Serialization; + +namespace JetBrains.Rd.Reflection +{ + /// + /// Abstraction to send over the protocol + /// + public sealed class RdOuterLifetime: RdReactiveBase + { + [NonSerialized] private LifetimeDefinition myLifetimeDefinition; + [NonSerialized] private bool myIsClientSide = false; + + public static implicit operator OuterLifetime (RdOuterLifetime rdOuterLifetime) + { + return rdOuterLifetime.myLifetimeDefinition; + } + + [UsedImplicitly] + public RdOuterLifetime() + { + } + + protected override void Init(Lifetime lifetime) + { + base.Init(lifetime); + myLifetimeDefinition = Lifetime.Define(lifetime); + myLifetimeDefinition.AllowTerminationUnderExecution = true; + Proto.Wire.Advise(lifetime, this); + + if (!myIsClientSide) + myLifetimeDefinition.Lifetime.OnTermination(() => Proto.Wire.Send(RdId, writer => writer.Write(1))); + } + + public override void OnWireReceived(UnsafeReader reader) + { + myLifetimeDefinition.Terminate(); + } + + /// + /// Used on a sender side to + /// + public void AttachToLifetime(Lifetime lifetime) + { + lifetime.OnTermination(myLifetimeDefinition); + } + + #region Intrinsic + + public static RdOuterLifetime Read(SerializationCtx ctx, UnsafeReader reader) + { + var id = reader.ReadRdId(); + var rdOuterLifetime = new RdOuterLifetime() + { + myIsClientSide = true + }.WithId(id); + return rdOuterLifetime; + } + + public static void Write(SerializationCtx ctx, UnsafeWriter writer, RdOuterLifetime value) + { + writer.Write(value.RdId); + } + + #endregion + } +} \ No newline at end of file diff --git a/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs b/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs new file mode 100644 index 000000000..1ba4246f2 --- /dev/null +++ b/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs @@ -0,0 +1,51 @@ +using JetBrains.Collections.Viewable; +using JetBrains.Lifetimes; +using JetBrains.Rd.Reflection; +using NUnit.Framework; +using Test.RdFramework.Reflection; + +namespace Test.RdFramework +{ + [TestFixture] + public class RdOuterLifetimeTest: RdReflectionTestBase + { + private Root myS; + private Root myC; + + [RdExt] + public sealed class Root : RdExtReflectionBindableBase + { + public IViewableProperty PolyProperty { get; } + } + + [RdModel] + public sealed class Model : RdReflectionBindableBase + { + public RdOuterLifetime OuterLifetime { get; } + } + + public override void SetUp() + { + base.SetUp(); + myS = SFacade.InitBind(new Root(), TestLifetime, ClientProtocol); + myC = CFacade.InitBind(new Root(), TestLifetime, ServerProtocol); + } + + [Test] + public void Test01() + { + Lifetime.Using(lifetime => + { + bool sTerminated = false; + var m = CFacade.Activator.Activate(); + myC.PolyProperty.Value = m; + var ld2 = OuterLifetime.DefineIntersection(lifetime, myS.PolyProperty.Value.OuterLifetime); + ld2.Lifetime.OnTermination(() => sTerminated = true); + + Lifetime.Using(outerLifetime => m.OuterLifetime.AttachToLifetime(outerLifetime)); + + Assert.IsTrue(sTerminated, "sTerminated"); + }); + } + } +} \ No newline at end of file From 452d3c636c530a9bf3b3384fd6618653a4a482f3 Mon Sep 17 00:00:00 2001 From: qx Date: Wed, 5 May 2021 14:18:16 +0300 Subject: [PATCH 3/3] RdOuterLifetime: attach to terminated lifetime --- .../RdFramework.Reflection/RdOuterLifetime.cs | 3 ++- rd-net/Test.RdFramework/RdOuterLifetimeTest.cs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/rd-net/RdFramework.Reflection/RdOuterLifetime.cs b/rd-net/RdFramework.Reflection/RdOuterLifetime.cs index b4e25fe2e..1fc45e5fe 100644 --- a/rd-net/RdFramework.Reflection/RdOuterLifetime.cs +++ b/rd-net/RdFramework.Reflection/RdOuterLifetime.cs @@ -46,7 +46,8 @@ public override void OnWireReceived(UnsafeReader reader) /// public void AttachToLifetime(Lifetime lifetime) { - lifetime.OnTermination(myLifetimeDefinition); + if (!lifetime.TryOnTermination(myLifetimeDefinition)) + myLifetimeDefinition.Terminate(); } #region Intrinsic diff --git a/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs b/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs index 1ba4246f2..5c8993634 100644 --- a/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs +++ b/rd-net/Test.RdFramework/RdOuterLifetimeTest.cs @@ -47,5 +47,23 @@ public void Test01() Assert.IsTrue(sTerminated, "sTerminated"); }); } + + [Test] + public void TestTerminatedLifetime01() + { + Lifetime.Using(lifetime => + { + bool sTerminated = false; + var m = CFacade.Activator.Activate(); + myC.PolyProperty.Value = m; + var ld2 = OuterLifetime.DefineIntersection(lifetime, myS.PolyProperty.Value.OuterLifetime); + ld2.Lifetime.OnTermination(() => sTerminated = true); + + var lifetimeDefinition = Lifetime.Define(lifetime); + lifetimeDefinition.Terminate(); + m.OuterLifetime.AttachToLifetime(lifetimeDefinition.Lifetime); + Assert.IsTrue(sTerminated, "sTerminated"); + }); + } } } \ No newline at end of file