From 4863010b471f0d13454cd03907c26dbb521a49b9 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Sat, 8 Mar 2025 19:46:59 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=20=D1=82=D0=B5=D0=BE=D1=80=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=BB=D0=B6=D0=B5=D0=BD=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D1=82=D1=8C=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B1=D1=8D=D0=BA=D1=8D?= =?UTF-8?q?=D0=BD=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example.Backend/Printer.cs | 3 + Example.Frontend/ClientBasedPrinter.cs | 12 +- Example.Shared/IPrinter.cs | 1 + mROA.Codegen/mROASourceGenerator.cs | 181 ++++++++++++++++-- mROA/Abstract/IMethodInvoker.cs | 4 +- .../Backend/BasicExecutionModule.cs | 100 ++++++---- mROA/Implementation/MethodInvoker.cs | 22 ++- mROA/Implementation/NetworkMessage.cs | 2 +- .../UniversalObjectIdentifier.cs | 8 + 9 files changed, 264 insertions(+), 69 deletions(-) diff --git a/Example.Backend/Printer.cs b/Example.Backend/Printer.cs index 170ba16..78bac1c 100644 --- a/Example.Backend/Printer.cs +++ b/Example.Backend/Printer.cs @@ -10,6 +10,8 @@ namespace Example.Backend { public string Name; + public decimal Resource { get; set; } + public string GetName() { return Name; @@ -30,5 +32,6 @@ namespace Example.Backend Console.WriteLine( "Dispose printer with name {0}, Dispose works, Dispose works, Dispose works, Dispose works,", Name); } + } } \ No newline at end of file diff --git a/Example.Frontend/ClientBasedPrinter.cs b/Example.Frontend/ClientBasedPrinter.cs index 888e802..754973c 100644 --- a/Example.Frontend/ClientBasedPrinter.cs +++ b/Example.Frontend/ClientBasedPrinter.cs @@ -8,16 +8,8 @@ namespace Example.Frontend { public class ClientBasedPrinter : IPrinter { - public int Prop { get; set; } - public int get_Prop() - { - return 1; - } - - public int set_Prop(int value) - { - return 1; - } + public decimal Resource { get; set; } + public string GetName() { Console.WriteLine("ClientBasedPrinter called from server!!!!!!!!!!! Vova likes that:)"); diff --git a/Example.Shared/IPrinter.cs b/Example.Shared/IPrinter.cs index 383961d..c1743db 100644 --- a/Example.Shared/IPrinter.cs +++ b/Example.Shared/IPrinter.cs @@ -9,6 +9,7 @@ namespace Example.Shared [SharedObjectInterface] public interface IPrinter : IDisposable, IShared { + decimal Resource { get; set; } string GetName(); Task Print(string text, bool someParameter, CancellationToken cancellationToken); event Action OnPrint; diff --git a/mROA.Codegen/mROASourceGenerator.cs b/mROA.Codegen/mROASourceGenerator.cs index 22a53ab..d31c93f 100644 --- a/mROA.Codegen/mROASourceGenerator.cs +++ b/mROA.Codegen/mROASourceGenerator.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Linq; -using System.Reflection; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -33,6 +31,7 @@ namespace mROA.Codegen var frontendContextRepo = new List(); List innerMethods = new List(); + List invokers = new List(); var declarations = classes.ToList().OrderBy(i => i.Identifier.Text).ToList(); foreach (var classDeclarationSyntax in declarations) { @@ -43,7 +42,7 @@ namespace mROA.Codegen var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); var className = classDeclarationSyntax.Identifier.Text; - innerMethods = CollectMembers(classSymbol); + innerMethods.AddRange(CollectMembers(classSymbol)); var associated = innerMethods.Select(i => i.AssociatedSymbol).Where(i => i != null) .Distinct(SymbolEqualityComparer.Default).Cast().ToList(); @@ -58,16 +57,16 @@ namespace mROA.Codegen var declaredMethods = new List(); var propertiesAccessMethods = new List<(string, IMethodSymbol)>(); - var propertiesImplementations = new List(associated.Count); + foreach (var method in innerMethods) { switch (method.MethodKind) { case MethodKind.PropertyGet or MethodKind.PropertySet: - GeneratePropertyMethod(method, innerMethods, propertiesAccessMethods); + GeneratePropertyMethod(method, innerMethods, propertiesAccessMethods, invokers); continue; default: - GenerateDeclaretedMethod(method, declaredMethods, innerMethods); + GenerateDeclaredMethod(method, declaredMethods, innerMethods, invokers); break; } } @@ -135,7 +134,8 @@ namespace {namespaceName} if (innerMethods.Count != 0) { - var methodsStringed = Array.Empty(); + var methodsStringed = invokers; + var coCodegenRepoCode = @$"// using System.Collections.Generic; @@ -143,6 +143,7 @@ using System.Reflection; using mROA.Abstract; using mROA.Implementation; using System; +using System.Threading; namespace mROA.Codegen {{ @@ -169,9 +170,11 @@ namespace mROA.Codegen }} }} "; +#if !DONT_ADD context.AddSource("CoCodegenMethodRepository.g.cs", SourceText.From(coCodegenRepoCode, Encoding.UTF8)); +#endif } - + if (frontendContextRepo.Count != 0) { var fronendRepoCode = @$"// @@ -198,8 +201,17 @@ namespace mROA.Codegen } } - private void GenerateDeclaretedMethod(IMethodSymbol method, List declaretedMethods, - List methods) + public static string Caster(ITypeSymbol type, string inner) + { + if (!type.IsValueType) + return inner + + " as " + + type.ToDisplayString(); + return $"({type.ToDisplayString()})" + inner; + } + + private void GenerateDeclaredMethod(IMethodSymbol method, List declaredMethods, + List methods, List invokers) { var index = methods.IndexOf(method); var sb = new StringBuilder(); @@ -259,40 +271,173 @@ namespace mROA.Codegen sb.AppendLine("\t\t}"); - declaretedMethods.Add(sb.ToString()); + declaredMethods.Add(sb.ToString()); + var parameterTypes = string.Join(", ", + $"{string.Join(", ", parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}"); + var level = "\t\t\t"; + + var parametersInsertList = new List(); + + for (var i = 0; i < method.Parameters.Length; i++) + { + var parameter = method.Parameters[i]; + switch (parameter.Type.Name) + { + case "CancellationToken": + parametersInsertList.Add("(CancellationToken)special[1]"); + break; + case "RequestContext": + parametersInsertList.Add("special[1] as RequestContext"); + break; + default: + parametersInsertList.Add(Caster(parameter.Type, + $"parameters[{parameters.IndexOf(parameter)}]")); + break; + } + } + + var parametersInsert = string.Join(", ", parametersInsertList); + var backend = string.Empty; + + var funcInvoking = string.Empty; + + if (isAsync && !isVoid) + funcInvoking = + $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(t.Result); }})"; + else if (isAsync && isVoid) + funcInvoking = + $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(null); }})"; + else if (!isAsync && isVoid) + { + funcInvoking = $@"{{ +{level} (i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}); +{level} return null; +{level} }}"; + } + else + { + funcInvoking = $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert})"; + } + + if (isAsync) + backend = $@"new mROA.Implementation.AsyncMethodInvoker +{level}{{ +{level} IsVoid = {isVoid.ToString().ToLower()}, +{(isVoid ? String.Empty : (level + "\t" + "ReturnType = typeof(" + ExtractTaskType(method.ReturnType)) + "),")} +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} Invoking = (i, parameters, special, post) => {funcInvoking}, +{level}}}"; + else + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = {isVoid.ToString().ToLower()}, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} Invoking = (i, parameters, special) => {funcInvoking} +{level}}}"; + + invokers.Add(backend); } private static Predicate ParameterFilter = i => i.Type.Name is "CancellationToken" or "RequestContext"; public void GeneratePropertyMethod(IMethodSymbol method, List methods, - List<(string, IMethodSymbol)> propsCollection) + List<(string, IMethodSymbol)> propsCollection, List invokers) { + var level = "\t\t\t"; var index = methods.IndexOf(method); - var sb = ""; + string frontend; + string backend = string.Empty; if (method.MethodKind == MethodKind.PropertyGet) { var parametersArray = ""; if (method.Parameters.Length != 0) { parametersArray = $", new object[] {{{string.Join(", ", method.Parameters.Select(p => p.Name))}}}"; + + var parameterTypes = string.Join(", ", + $"{string.Join(", ", method.Parameters.Select(p => "typeof(" + p.Type.ToDisplayString() + ")"))}"); + var parameterInserts = string.Join(", ", + method.Parameters.Select(p => + { + return Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"); + // if (!p.Type.IsValueType) + // return "parameters[" + method.Parameters.IndexOf(p) + "] as " + + // p.Type.ToDisplayString(); + // return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]"; + } + )); + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = false, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}], +{level}}}"; + } + else + { + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = false, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} Invoking = (i, _, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}, +{level}}}"; } - sb = + frontend = $"get => GetResultAsync<{method.ReturnType.ToDisplayString()}>({index}{parametersArray}).GetAwaiter().GetResult();"; } else { var parametersArray = "value"; - if (method.Parameters.Length != 0) + if (method.Parameters.Length != 1) { parametersArray = string.Join(", ", method.Parameters.Select(p => p.Name)); + + var parameterTypes = string.Join(", ", + $"{string.Join(", ", method.Parameters.Select(p => $"typeof({p.Type.ToDisplayString()})"))}"); + var parameterInserts = string.Join(", ", + method.Parameters.Take(method.Parameters.Length - 1).Select(p => + { + return Caster(p.Type, "parameters[" + method.Parameters.IndexOf(p) + "]"); + // if (!p.Type.IsValueType) + // return "parameters[" + method.Parameters.IndexOf(p) + "] as " + + // p.Type.ToDisplayString(); + // return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]"; + } + )); + + // var valueInsert = !method.Parameters.Last().Type.IsValueType + // ? "parameters[" + (method.Parameters.Length - 1) + "] as " + + // method.Parameters.Last().Type.ToDisplayString() + // : $"({method.Parameters.Last().Type.ToDisplayString()})parameters[{method.Parameters.Length - 1}]"; + var valueInsert = Caster(method.Parameters.Last().Type, + "parameters[" + (method.Parameters.Length - 1) + "]"); + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = false, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} ParameterTypes = new Type[] {{ {parameterTypes} }}, +{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}] = {valueInsert}, +{level}}}"; + } + else + { + backend = $@"new mROA.Implementation.MethodInvoker +{level}{{ +{level} IsVoid = false, +{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}), +{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")}, +{level}}}"; } - sb = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();"; + frontend = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();"; } - propsCollection.Add((sb.ToString(), method)); + propsCollection.Add((frontend, method)); + invokers.Add(backend); } private static string ToFullString(IParameterSymbol parameter) @@ -307,7 +452,7 @@ namespace mROA.Codegen private string ExtractTaskType(ITypeSymbol taskType) { - return (taskType as INamedTypeSymbol).TypeParameters[0].ToDisplayString(); + return (taskType as INamedTypeSymbol).TypeArguments[0].ToDisplayString(); } public void Initialize(GeneratorInitializationContext context) diff --git a/mROA/Abstract/IMethodInvoker.cs b/mROA/Abstract/IMethodInvoker.cs index 439434a..eb5e182 100644 --- a/mROA/Abstract/IMethodInvoker.cs +++ b/mROA/Abstract/IMethodInvoker.cs @@ -3,12 +3,10 @@ using System; namespace mROA.Abstract { public interface IMethodInvoker - { - bool IsAsync { get; } + { bool IsVoid { get; } Type[] ParameterTypes { get; } Type? ReturnType { get; } - object? Invoke(object instance, object?[]? parameters, object[] special); Type SuitableType { get; } } } \ No newline at end of file diff --git a/mROA/Implementation/Backend/BasicExecutionModule.cs b/mROA/Implementation/Backend/BasicExecutionModule.cs index 31e9ee1..3e94cb2 100644 --- a/mROA/Implementation/Backend/BasicExecutionModule.cs +++ b/mROA/Implementation/Backend/BasicExecutionModule.cs @@ -58,7 +58,7 @@ namespace mROA.Implementation.Backend throw new NullReferenceException("Can't find cancellation for this request"); cts.Cancel(); _cancellationRepo.FreeCancelation(command.Id); - + return new FinalCommandExecution { Id = command.Id @@ -75,7 +75,7 @@ namespace mROA.Implementation.Backend if (context == null) throw new NullReferenceException("Instance can't be null"); - + object?[]? castedParams = null; @@ -87,18 +87,19 @@ namespace mROA.Implementation.Backend castedParams[i] = _serialization.Cast(command.Parameters![i], invoker.ParameterTypes[i]); } } - + var execContext = new RequestContext(command.Id, representationModule.Id); - if (invoker is { IsAsync: true, IsVoid: false }) - return TypedExecuteAsync(invoker, context, castedParams, command, _cancellationRepo, + if (invoker is AsyncMethodInvoker { IsVoid: false } asyncNonVoidMethodInvoker) + return TypedExecuteAsync(asyncNonVoidMethodInvoker, context, castedParams, command, + _cancellationRepo, representationModule, execContext); - if (invoker.IsAsync) - return ExecuteAsync(invoker, context, castedParams, command, _cancellationRepo, + if (invoker is AsyncMethodInvoker asyncMethodInvoker) + return ExecuteAsync(asyncMethodInvoker, context, castedParams, command, _cancellationRepo, representationModule, execContext); - var result = Execute(invoker, context, castedParams, command, execContext); + var result = Execute((invoker as MethodInvoker)!, context, castedParams!, command, execContext); if (command.CommandId == -1) { #if TRACE @@ -119,7 +120,7 @@ namespace mROA.Implementation.Backend } } - private static ICommandExecution Execute(IMethodInvoker invoker, object instance, object?[] parameter, + private static ICommandExecution Execute(MethodInvoker invoker, object instance, object?[] parameter, ICallRequest command, RequestContext executionContext) { try @@ -150,7 +151,7 @@ namespace mROA.Implementation.Backend } } - private ICommandExecution ExecuteAsync(IMethodInvoker invoker, object instance, object?[]? parameters, + private ICommandExecution ExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, ICallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule, RequestContext executionContext) { @@ -162,10 +163,7 @@ namespace mROA.Implementation.Backend #endif try { - var result = (Task)invoker.Invoke(instance, parameters, new object[] { executionContext, token })!; - - - result.ContinueWith(_ => + invoker.Invoke(instance, parameters, new object[] { executionContext, token }, _ => { if (token.IsCancellationRequested) return; @@ -182,7 +180,26 @@ namespace mROA.Implementation.Backend multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); multiClientOwnershipRepository?.FreeOwnership(); - }, token); + }); + + // result.ContinueWith(_ => + // { + // if (token.IsCancellationRequested) + // return; + // + // var payload = new FinalCommandExecution + // { + // Id = command.Id + // }; + // _cancellationRepo?.FreeCancelation(command.Id); + // + // var multiClientOwnershipRepository = + // TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + // + // multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); + // representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); + // multiClientOwnershipRepository?.FreeOwnership(); + // }, token); return new AsyncCommandExecution { @@ -199,7 +216,7 @@ namespace mROA.Implementation.Backend } } - private ICommandExecution TypedExecuteAsync(IMethodInvoker invoker, object instance, object?[]? parameters, + private ICommandExecution TypedExecuteAsync(AsyncMethodInvoker invoker, object instance, object?[]? parameters, ICallRequest command, ICancellationRepository cancellationRepository, IRepresentationModule representationModule, RequestContext executionContext) { @@ -209,25 +226,42 @@ namespace mROA.Implementation.Backend var token = tokenSource.Token; try { - var result = - (Task)invoker.Invoke(instance, parameters, new object[] { executionContext, token })!; - - result.ContinueWith(t => - { - var finalResult = t.GetType().GetProperty("Result")?.GetValue(t); - var payload = new FinalCommandExecution + invoker.Invoke(instance, parameters, new object[] { executionContext, token }, + finalResult => { - Id = command.Id, - Result = finalResult - }; - _cancellationRepo!.FreeCancelation(command.Id); + var payload = new FinalCommandExecution + { + Id = command.Id, + Result = finalResult + }; + _cancellationRepo!.FreeCancelation(command.Id); - var multiClientOwnershipRepository = - TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; - multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); - representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); - multiClientOwnershipRepository?.FreeOwnership(); - }, token); + var multiClientOwnershipRepository = + TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); + representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, + payload); + multiClientOwnershipRepository?.FreeOwnership(); + }); + + + + // result.ContinueWith(t => + // { + // var finalResult = t.GetType().GetProperty("Result")?.GetValue(t); + // var payload = new FinalCommandExecution + // { + // Id = command.Id, + // Result = finalResult + // }; + // _cancellationRepo!.FreeCancelation(command.Id); + // + // var multiClientOwnershipRepository = + // TransmissionConfig.OwnershipRepository as MultiClientOwnershipRepository; + // multiClientOwnershipRepository?.RegisterOwnership(representationModule.Id); + // representationModule.PostCallMessage(command.Id, MessageType.FinishedCommandExecution, payload); + // multiClientOwnershipRepository?.FreeOwnership(); + // }, token); return new AsyncCommandExecution { diff --git a/mROA/Implementation/MethodInvoker.cs b/mROA/Implementation/MethodInvoker.cs index 8299a0a..4edea72 100644 --- a/mROA/Implementation/MethodInvoker.cs +++ b/mROA/Implementation/MethodInvoker.cs @@ -5,11 +5,10 @@ namespace mROA.Implementation { public class MethodInvoker : IMethodInvoker { - public bool IsAsync { get; set; } public bool IsVoid { get; set; } public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; public Type? ReturnType { get; set; } - public Func Invoking { get; set; } = (_, _, _) => null; + public Func Invoking { get; set; } = (_, _, _) => null; public object? Invoke(object instance, object?[]? parameters, object[] special) { @@ -17,10 +16,9 @@ namespace mROA.Implementation } public Type SuitableType { get; set; } = null!; - + public static readonly IMethodInvoker Dispose = new MethodInvoker { - IsAsync = false, IsVoid = true, ReturnType = null, Invoking = (instance, _, _) => @@ -31,4 +29,20 @@ namespace mROA.Implementation SuitableType = typeof(IDisposable) }; } + + public class AsyncMethodInvoker : IMethodInvoker + { + public bool IsVoid { get; set; } + public Type[] ParameterTypes { get; set; } = Type.EmptyTypes; + public Type? ReturnType { get; set; } + public Type SuitableType { get; set; } + + public Action> Invoking { get; set; } = + (_, _, _, post) => { post.Invoke(null); }; + + public void Invoke(object instance, object?[]? parameters, object[] special, Action postInvokeAction) + { + Invoking(instance, parameters, special, postInvokeAction); + } + } } \ No newline at end of file diff --git a/mROA/Implementation/NetworkMessage.cs b/mROA/Implementation/NetworkMessage.cs index 7b04c12..dd71589 100644 --- a/mROA/Implementation/NetworkMessage.cs +++ b/mROA/Implementation/NetworkMessage.cs @@ -15,6 +15,6 @@ namespace mROA.Implementation public enum MessageType { - Unknown, FinishedCommandExecution, ExceptionCommandExecution, AsyncCommandExecution, CallRequest, IdAssigning, CancelRequest + Unknown, FinishedCommandExecution, ExceptionCommandExecution, AsyncCommandExecution, CallRequest, IdAssigning, CancelRequest, EventRequest } } \ No newline at end of file diff --git a/mROA/Implementation/UniversalObjectIdentifier.cs b/mROA/Implementation/UniversalObjectIdentifier.cs index 781567e..694636c 100644 --- a/mROA/Implementation/UniversalObjectIdentifier.cs +++ b/mROA/Implementation/UniversalObjectIdentifier.cs @@ -1,10 +1,18 @@ using System; +using mROA.Abstract; namespace mROA.Implementation { #pragma warning disable CS8618, CS9264 public struct UniversalObjectIdentifier : IEquatable { + private static IMethodInvoker x = new MethodInvoker + { + IsVoid = false, + ReturnType = typeof(void), + ParameterTypes = Type.EmptyTypes + + }; public int ContextId; public int OwnerId;