2025-03-08 12:10:12 +03:00
// #define DONT_ADD
2025-02-22 09:28:59 +03:00
using System ;
2025-02-04 22:10:48 +03:00
using System.Collections.Generic ;
2025-02-04 19:02:39 +03:00
using System.Collections.Immutable ;
2025-03-25 21:27:54 +03:00
using System.IO ;
2025-02-04 19:02:39 +03:00
using System.Linq ;
using System.Text ;
using Microsoft.CodeAnalysis ;
using Microsoft.CodeAnalysis.CSharp.Syntax ;
using Microsoft.CodeAnalysis.Text ;
2025-03-25 21:27:54 +03:00
using mROA.CodegenTools ;
2025-02-04 19:02:39 +03:00
2025-02-20 16:10:09 +03:00
namespace mROA.Codegen
2025-02-04 19:02:39 +03:00
{
2025-02-20 16:10:09 +03:00
/// <summary>
/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute.
/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead.
/// </summary>
[Generator]
public class mROASourceGenerator : ISourceGenerator
{
2025-03-25 21:27:54 +03:00
private TemplateDocument MethodRepoTemplate ;
private TemplateDocument ClassTemplateOriginal ;
private TemplateDocument ClassTemplate ;
2025-03-26 09:58:43 +03:00
private TemplateDocument InterfaceTemplateOriginal ;
private TemplateDocument InterfaceTemplate ;
2025-03-25 21:40:12 +03:00
private TemplateDocument BinderTemplate ;
2025-03-27 20:06:09 +03:00
private TemplateDocument MethodInvokerOriginal ;
2025-03-10 00:00:24 +03:00
private static Predicate < IParameterSymbol > ParameterFilter =
i => i . Type . Name is "CancellationToken" or "RequestContext" ;
private static Predicate < ITypeSymbol > ParameterFilterForType =
i => i . Name is "CancellationToken" or "RequestContext" ;
public void Initialize ( GeneratorInitializationContext context )
{
2025-03-25 21:27:54 +03:00
MethodRepoTemplate = TemplateReader . FromEmbeddedResource ( "MethodRepo.cstmpl" );
2025-03-27 20:06:09 +03:00
MethodInvokerOriginal =
(( InnerTemplateSection ) MethodRepoTemplate [ "syncInvoker" ])?. InnerTemplate ;
2025-03-25 21:27:54 +03:00
ClassTemplateOriginal = TemplateReader . FromEmbeddedResource ( "RemoteEndpoint.cstmpl" );
2025-03-25 21:40:12 +03:00
BinderTemplate = TemplateReader . FromEmbeddedResource ( "RemoteTypeBinder.cstmpl" );
2025-03-26 09:58:43 +03:00
InterfaceTemplateOriginal = TemplateReader . FromEmbeddedResource ( "PartialInterface.cstmpl" );
2025-03-10 00:00:24 +03:00
}
public void Execute ( GeneratorExecutionContext context )
{
2025-03-25 21:27:54 +03:00
try
2025-03-10 00:00:24 +03:00
{
2025-03-25 21:27:54 +03:00
var trees = context . Compilation . SyntaxTrees ;
2025-03-10 00:00:24 +03:00
2025-03-25 21:27:54 +03:00
var interfaces = new List < InterfaceDeclarationSyntax >();
foreach ( var tree in trees )
2025-03-10 00:00:24 +03:00
{
2025-03-25 21:27:54 +03:00
var node = tree . GetRoot () as CompilationUnitSyntax ;
2025-03-10 00:00:24 +03:00
2025-03-25 21:27:54 +03:00
foreach ( var member in node . Members )
{
if ( member is InterfaceDeclarationSyntax ids )
{
interfaces . Add ( ids );
}
else if ( member is NamespaceDeclarationSyntax nds )
{
foreach ( var inside in nds . Members )
if ( inside is InterfaceDeclarationSyntax ids2 )
if ( ContainsSOIAttribute ( ids2 . AttributeLists , context , ids2 ))
interfaces . Add ( ids2 );
}
2025-03-10 00:00:24 +03:00
}
}
2025-03-25 21:27:54 +03:00
GenerateCode ( context , context . Compilation , interfaces . ToImmutableArray ());
}
2025-03-25 21:40:12 +03:00
catch ( Exception e )
2025-03-25 21:27:54 +03:00
{
Console . WriteLine ( "ERROR: Unable to load method repository" );
}
2025-03-10 00:00:24 +03:00
}
2025-02-20 16:10:09 +03:00
private void GenerateCode ( GeneratorExecutionContext context , Compilation compilation ,
ImmutableArray < InterfaceDeclarationSyntax > classes )
{
2025-03-08 22:31:40 +03:00
List < IMethodSymbol > totalMethods = new List < IMethodSymbol >();
2025-03-06 14:10:52 +03:00
2025-03-10 00:00:24 +03:00
2025-03-08 19:46:59 +03:00
List < string > invokers = new List < string >();
2025-02-20 16:10:09 +03:00
var declarations = classes . ToList (). OrderBy ( i => i . Identifier . Text ). ToList ();
foreach ( var classDeclarationSyntax in declarations )
2025-03-07 09:01:21 +03:00
{
2025-02-20 16:10:09 +03:00
var semanticModel = compilation . GetSemanticModel ( classDeclarationSyntax . SyntaxTree );
2025-02-04 19:02:39 +03:00
2025-02-20 16:10:09 +03:00
if ( semanticModel . GetDeclaredSymbol ( classDeclarationSyntax ) is not INamedTypeSymbol classSymbol )
continue ;
2025-03-08 22:31:40 +03:00
List < IMethodSymbol > innerMethods = new List < IMethodSymbol >();
2025-03-07 09:01:21 +03:00
2025-03-06 14:10:52 +03:00
var namespaceName = classSymbol . ContainingNamespace . ToDisplayString ();
2025-02-20 16:10:09 +03:00
var className = classDeclarationSyntax . Identifier . Text ;
2025-03-08 22:31:40 +03:00
innerMethods = CollectMembers ( classSymbol );
2025-03-08 12:10:12 +03:00
var associated = innerMethods . Select ( i => i . AssociatedSymbol ). Where ( i => i != null )
. Distinct ( SymbolEqualityComparer . Default ). Cast < ISymbol >(). ToList ();
innerMethods = innerMethods . Where ( i =>
i . MethodKind != MethodKind . EventAdd && i . MethodKind != MethodKind . EventRemove )
2025-03-07 15:46:22 +03:00
. ToList ();
2025-03-08 22:31:40 +03:00
totalMethods . AddRange ( innerMethods );
2025-03-10 00:00:24 +03:00
2025-02-20 16:10:09 +03:00
var originalName = className ;
2025-02-04 22:10:48 +03:00
2025-03-07 09:01:21 +03:00
className = className . TrimStart ( 'I' ) + "RemoteEndpoint" ;
2025-03-06 14:10:52 +03:00
2025-03-25 21:27:54 +03:00
ClassTemplate = ( TemplateDocument ) ClassTemplateOriginal . Clone ();
2025-03-07 15:46:22 +03:00
var declaredMethods = new List < string >();
var propertiesAccessMethods = new List <( string , IMethodSymbol )>();
2025-03-08 19:46:59 +03:00
2025-03-07 15:46:22 +03:00
foreach ( var method in innerMethods )
{
switch ( method . MethodKind )
2025-03-06 14:10:52 +03:00
{
2025-03-07 15:46:22 +03:00
case MethodKind . PropertyGet or MethodKind . PropertySet :
2025-03-10 00:00:24 +03:00
GeneratePropertyMethod ( method , propertiesAccessMethods , invokers ,
classSymbol );
2025-03-06 14:10:52 +03:00
continue ;
2025-03-07 15:46:22 +03:00
default :
2025-03-10 00:00:24 +03:00
GenerateDeclaredMethod ( method , declaredMethods , invokers , classSymbol );
2025-03-07 15:46:22 +03:00
break ;
2025-03-06 14:10:52 +03:00
}
2025-03-07 15:46:22 +03:00
}
2025-02-04 23:19:24 +03:00
2025-03-08 12:10:12 +03:00
foreach ( var symbol in associated )
2025-03-07 15:46:22 +03:00
{
2025-03-08 12:10:12 +03:00
switch ( symbol )
{
case IPropertySymbol propertySymbol :
var setter = propertiesAccessMethods . FirstOrDefault ( i =>
i . Item2 . AssociatedSymbol !. Name == propertySymbol . Name && i . Item2 . ReturnsVoid );
var getter = propertiesAccessMethods . FirstOrDefault ( i =>
i . Item2 . AssociatedSymbol !. Name == propertySymbol . Name && ! i . Item2 . ReturnsVoid );
string impl ;
if ( propertySymbol . IsIndexer )
{
impl =
$"public {propertySymbol.Type.ToDisplayString()} this[{string.Join(" , ", propertySymbol.Parameters.Select(p => p.ToDisplayString()))}] {{ {getter.Item1} {setter.Item1} }}" ;
}
else
{
impl =
$"public {propertySymbol.Type.ToDisplayString()} {symbol.Name} {{ {getter.Item1} {setter.Item1} }}" ;
}
2025-03-26 14:43:06 +03:00
2025-03-25 21:27:54 +03:00
ClassTemplate . Insert ( "methods" , impl );
// declaredMethods.Add(impl);
2025-03-08 12:10:12 +03:00
break ;
case IEventSymbol eventSymbol :
2025-03-26 14:43:06 +03:00
ClassTemplate . Insert ( "methods" ,
$"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};" );
2025-03-25 21:27:54 +03:00
// declaredMethods.Add(
// $"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};");
2025-03-08 12:10:12 +03:00
break ;
}
2025-02-20 16:10:09 +03:00
}
2025-03-25 21:40:12 +03:00
GenerateEventImplementation ( classSymbol , invokers , context );
2025-03-10 00:00:24 +03:00
2025-03-25 21:27:54 +03:00
ClassTemplate . AddDefine ( "className" , className );
ClassTemplate . AddDefine ( "originalName" , originalName );
ClassTemplate . AddDefine ( "namespaceName" , namespaceName );
2025-03-26 14:43:06 +03:00
2025-03-25 21:27:54 +03:00
var code = ClassTemplate . Compile ();
2025-02-04 19:02:39 +03:00
2025-02-04 22:10:48 +03:00
2025-02-20 16:10:09 +03:00
// Add the source code to the compilation.
2025-03-08 12:10:12 +03:00
#if ! DONT_ADD
context . AddSource ( $"{className}.g.cs" , SourceText . From ( code , Encoding . UTF8 ));
#endif
2025-03-25 21:40:12 +03:00
BinderTemplate . Insert ( "remoteTypePair" ,
2025-02-20 16:10:09 +03:00
$"{{ typeof({classSymbol.ToDisplayString()}), typeof({namespaceName}.{className}) }}" );
}
2025-03-08 22:31:40 +03:00
if ( totalMethods . Count != 0 )
2025-02-20 16:10:09 +03:00
{
2025-03-08 19:46:59 +03:00
var methodsStringed = invokers ;
2025-03-10 00:00:24 +03:00
2025-03-27 20:06:09 +03:00
2025-02-20 16:10:09 +03:00
2025-03-25 21:27:54 +03:00
var coCodegenRepoCode = MethodRepoTemplate . Compile ();
2025-03-08 19:46:59 +03:00
#if ! DONT_ADD
2025-03-06 14:10:52 +03:00
context . AddSource ( "CoCodegenMethodRepository.g.cs" , SourceText . From ( coCodegenRepoCode , Encoding . UTF8 ));
2025-03-08 19:46:59 +03:00
#endif
2025-02-20 16:10:09 +03:00
}
2025-03-08 22:31:40 +03:00
2025-03-25 21:40:12 +03:00
if ( BinderTemplate [ "remoteTypePair+" ] != null )
2025-02-20 16:10:09 +03:00
{
2025-03-25 21:40:12 +03:00
var fronendRepoCode = BinderTemplate . Compile ();
2025-03-08 12:10:12 +03:00
#if ! DONT_ADD
context . AddSource ( "RemoteTypeBinder.g.cs" , SourceText . From ( fronendRepoCode , Encoding . UTF8 ));
#endif
2025-02-20 16:10:09 +03:00
}
2025-02-06 09:18:52 +03:00
}
2025-03-26 14:43:06 +03:00
private void GenerateEventImplementation ( INamedTypeSymbol classSymbol , List < string > invokers ,
GeneratorExecutionContext context )
2025-03-10 00:00:24 +03:00
{
var events = classSymbol . AllInterfaces . Add ( classSymbol ). SelectMany ( i => i . GetMembers ())
. OfType < IEventSymbol >(). ToList ();
if ( events . Count == 0 )
return ;
2025-03-26 14:43:06 +03:00
2025-03-26 09:58:43 +03:00
InterfaceTemplate = ( TemplateDocument ) InterfaceTemplateOriginal . Clone ();
InterfaceTemplate . AddDefine ( "name" , classSymbol . Name );
InterfaceTemplate . AddDefine ( "namespace" , classSymbol . ContainingNamespace . ToDisplayString ());
2025-03-10 16:35:56 +03:00
var singleEventBinder = new List < string >( events . Count );
2025-03-27 17:41:16 +03:00
var objectBinderTemplate =
( TemplateDocument )(( InnerTemplateSection ) BinderTemplate [ "objectBinderTemplate" ]). InnerTemplate . Clone ();
2025-03-10 00:00:24 +03:00
for ( int i = 0 ; i < events . Count ; i ++)
{
var currentEvent = events [ i ];
var additionalMethod = GenerateMethodExternalCaller ( currentEvent , out var signature );
2025-03-25 21:27:54 +03:00
ClassTemplate . Insert ( "methods" , additionalMethod );
// declaredMethods.Add(additionalMethod);
2025-03-26 09:58:43 +03:00
InterfaceTemplate . Insert ( "signature" , signature );
2025-03-10 00:00:24 +03:00
GenerateEventCode ( currentEvent , invokers , classSymbol );
2025-03-26 14:43:06 +03:00
GenerateBinderCode ( currentEvent , invokers , classSymbol , objectBinderTemplate );
2025-03-10 00:00:24 +03:00
}
2025-03-27 17:41:16 +03:00
2025-03-26 14:43:06 +03:00
objectBinderTemplate . AddDefine ( "type" , classSymbol . ToDisplayString ());
2025-03-26 09:58:43 +03:00
var partialInterface = InterfaceTemplate . Compile ();
2025-03-26 14:43:06 +03:00
var binder = objectBinderTemplate . Compile ();
2025-03-25 21:40:12 +03:00
BinderTemplate . Insert ( "eventBinder" , binder );
2025-03-10 00:00:24 +03:00
#if ! DONT_ADD
context . AddSource ( $"{classSymbol.Name}.g.cs" , SourceText . From ( partialInterface , Encoding . UTF8 ));
#endif
}
private string GenerateMethodExternalCaller ( IEventSymbol eventSymbol , out string interfaceSignature )
{
var level = "\t\t" ;
var parameters = ( eventSymbol . Type as INamedTypeSymbol ). TypeArguments ;
var parameterIndex = 0 ;
var parametersDeclaration =
string . Join ( ", " , parameters . Select ( i => $"{i.ToDisplayString()} p{parameterIndex++}" ));
var signature = $@"public void {EventExternalName(eventSymbol)}({parametersDeclaration})" ;
2025-03-26 09:58:43 +03:00
interfaceSignature = signature + ";" ;
2025-03-10 00:00:24 +03:00
var caller = $@"{signature}
{level}{{
{level} {eventSymbol.Name}?.Invoke({string.Join(" , ", Enumerable.Range(0, parameterIndex).Select(i => " p " + i))});
{ level }}}
";
return caller ;
}
public static string EventExternalName ( IEventSymbol eventSymbol ) => $"{eventSymbol.Name}External" ;
private static string Caster ( ITypeSymbol type , string inner )
2025-03-08 19:46:59 +03:00
{
if (! type . IsValueType )
return inner +
" as " +
type . ToDisplayString ();
return $"({type.ToDisplayString()})" + inner ;
}
2025-03-10 00:00:24 +03:00
private void GenerateDeclaredMethod ( IMethodSymbol method , List < string > declaredMethods , List < string > invokers ,
INamedTypeSymbol baseInterace )
2025-03-07 15:46:22 +03:00
{
2025-03-10 00:00:24 +03:00
var index = invokers . Count ;
2025-03-07 15:46:22 +03:00
var sb = new StringBuilder ();
bool isParametrized ;
bool isAsync ;
bool isVoid ;
List < IParameterSymbol >? parameters ;
switch ( method . ReturnType )
{
case INamedTypeSymbol namedType :
isAsync = namedType . Name == "Task" ;
isVoid = isAsync && namedType . TypeParameters . Length == 0 || namedType . Name == "Void" ;
parameters = method . Parameters . ToList ();
2025-03-08 12:10:12 +03:00
parameters . RemoveAll ( ParameterFilter );
2025-03-07 15:46:22 +03:00
isParametrized = parameters . Count != 0 ;
break ;
case IArrayTypeSymbol :
isAsync = false ;
isVoid = false ;
2025-03-08 12:10:12 +03:00
parameters = new List < IParameterSymbol >();
2025-03-07 15:46:22 +03:00
isParametrized = method . Parameters . Length != 0 ;
break ;
default :
return ;
}
//Creating signature
sb . AppendLine ( "public" + ( isAsync
? " async "
: " " ) +
$"{method.ReturnType.ToDisplayString()} {method.Name}({string.Join(" , ", method.Parameters.Select(ToFullString))}){{" );
var prefix = isAsync ? "await " : "" ;
var postfix = ! isAsync ? isVoid ? ".Wait()" : ".GetAwaiter().GetResult()" : "" ;
var parameterLink = isParametrized
2025-03-08 12:10:12 +03:00
? ", new object[] {" + string . Join ( ", " , parameters . Select ( i => i . Name )) + "}"
2025-03-07 15:46:22 +03:00
: string . Empty ;
2025-03-08 12:10:12 +03:00
var tokenInsert = isAsync && method . Parameters . FirstOrDefault ( i => i . Type . Name == "CancellationToken" ) is
{ } tokenSymbol
? ", cancellationToken : " + tokenSymbol . Name
2025-03-07 15:46:22 +03:00
: String . Empty ;
var caller = isVoid
? $"CallAsync({index}{parameterLink}{tokenInsert})"
: isAsync
? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"
: $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" ;
if (! isVoid )
prefix = "return " + prefix ;
sb . AppendLine ( "\t\t\t" + prefix + caller + postfix + ";" );
sb . AppendLine ( "\t\t}" );
2025-03-25 21:27:54 +03:00
ClassTemplate . Insert ( "methods" , sb . ToString ());
2025-03-07 15:46:22 +03:00
2025-03-25 21:27:54 +03:00
// declaredMethods.Add(sb.ToString());
2025-03-08 19:46:59 +03:00
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", parameters.Select(p => $" typeof ({ p . Type . ToDisplayString ()}) "))}" );
var level = "\t\t\t" ;
var parametersInsertList = new List < string >();
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" :
2025-03-10 00:12:13 +03:00
parametersInsertList . Add ( "special[0] as RequestContext" );
2025-03-08 19:46:59 +03:00
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 )
2025-03-27 17:41:16 +03:00
{
var invokerTemplate = ( TemplateDocument )(( InnerTemplateSection ) MethodRepoTemplate [ "asyncInvoker" ]). InnerTemplate . Clone ();
invokerTemplate . AddDefine ( "isVoid" , isVoid . ToString (). ToLower ());
invokerTemplate . AddDefine ( "returnType" , ExtractTaskType ( method . ReturnType ));
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , funcInvoking );
backend = invokerTemplate . Compile ();
}
2025-03-08 19:46:59 +03:00
else
2025-03-27 17:41:16 +03:00
{
2025-03-27 20:06:09 +03:00
var invokerTemplate = ( TemplateDocument ) MethodInvokerOriginal . Clone ();
2025-03-27 17:41:16 +03:00
invokerTemplate . AddDefine ( "isVoid" , isVoid . ToString (). ToLower ());
invokerTemplate . AddDefine ( "returnType" , isVoid ? "void" : method . ReturnType . ToDisplayString ());
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , funcInvoking );
backend = invokerTemplate . Compile ();
}
2025-03-25 21:27:54 +03:00
MethodRepoTemplate . Insert ( "invoker" , backend );
2025-03-08 19:46:59 +03:00
invokers . Add ( backend );
2025-03-07 15:46:22 +03:00
}
2025-03-10 16:35:56 +03:00
private void GenerateBinderCode ( IEventSymbol eventSymbol , List < string > invokers , INamedTypeSymbol baseType ,
2025-03-26 14:43:06 +03:00
TemplateDocument document )
2025-03-10 16:35:56 +03:00
{
2025-03-27 17:41:16 +03:00
var eventBinderTemplate =
( TemplateDocument )(( InnerTemplateSection ) document [ "eventBinderTemplate" ]!). InnerTemplate . Clone ();
2025-03-10 16:35:56 +03:00
var index = invokers . Count - 1 ;
2025-03-26 14:43:06 +03:00
var parameters = ( eventSymbol . Type as INamedTypeSymbol )!. TypeArguments . ToList ();
2025-03-10 16:35:56 +03:00
int parameterIndex = 0 ;
var parametersDeclaration = string . Join ( ", " ,
JoinWithComa ( Enumerable . Range ( 0 , parameters . Count ). Select ( i => "p" + i ++)));
var transferParameters =
2025-03-10 17:16:13 +03:00
JoinWithComa ( parameters . Where ( i => ! ParameterFilterForType ( i ))
. Select ( i => "p" + parameters . IndexOf ( i )));
2025-03-10 16:35:56 +03:00
2025-03-26 14:43:06 +03:00
string callFilter ;
2025-03-10 16:35:56 +03:00
var requestIndex = parameters . FindIndex ( i => i . Name == "RequestContext" );
if ( requestIndex != - 1 )
{
2025-03-13 08:43:48 +03:00
callFilter = $"\n\r\t\t\tif(ownerId == p{requestIndex}.OwnerId) return;" ;
2025-03-26 14:43:06 +03:00
eventBinderTemplate . AddDefine ( "callFilter" , callFilter );
2025-03-10 16:35:56 +03:00
}
2025-03-26 14:43:06 +03:00
eventBinderTemplate . AddDefine ( "type" , baseType . ToDisplayString ());
eventBinderTemplate . AddDefine ( "eventName" , eventSymbol . Name );
eventBinderTemplate . AddDefine ( "parametersDeclaration" , parametersDeclaration );
eventBinderTemplate . AddDefine ( "commandId" , index . ToString ());
eventBinderTemplate . AddDefine ( "transferParameters" , transferParameters );
var eventBinderCode = eventBinderTemplate . Compile ();
document . Insert ( "eventBinder" , eventBinderCode );
2025-03-10 16:35:56 +03:00
}
public static string JoinWithComa ( IEnumerable < string > parts ) => string . Join ( ", " , parts );
2025-03-10 00:00:24 +03:00
private void GenerateEventCode ( IEventSymbol eventSymbol , List < string > invokers , ITypeSymbol baseInterface )
{
var level = "\t\t\t" ;
2025-03-08 12:10:12 +03:00
2025-03-10 00:00:24 +03:00
var parameters = ( eventSymbol . Type as INamedTypeSymbol ). TypeArguments ;
var parsingParameters = parameters . RemoveAll ( ParameterFilterForType ). ToList ();
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", parsingParameters.Select(p => $" typeof ({ p . ToDisplayString ()}) "))}" );
var parametersInsertList = new List < string >();
for ( var i = 0 ; i < parameters . Length ; i ++)
{
var parameter = parameters [ i ];
switch ( parameter . Name )
{
case "CancellationToken" :
parametersInsertList . Add ( "(CancellationToken)special[1]" );
break ;
case "RequestContext" :
2025-03-11 19:42:10 +03:00
parametersInsertList . Add ( "special[0] as RequestContext" );
2025-03-10 00:00:24 +03:00
break ;
default :
parametersInsertList . Add ( Caster ( parameter ,
$"parameters[{parameters.IndexOf(parameter)}]" ));
break ;
}
}
2025-03-27 20:06:09 +03:00
2025-03-10 00:00:24 +03:00
var parametersInsert = string . Join ( ", " , parametersInsertList );
2025-03-27 20:06:09 +03:00
var funcInvoking = $@"{{
2025-03-10 00:00:24 +03:00
{level} (i as {baseInterface.ToDisplayString()}).{EventExternalName(eventSymbol)}({parametersInsert});
{level} return null;
2025-03-27 20:06:09 +03:00
{level} }}" ;
var invokerTemplate = ( TemplateDocument ) MethodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "true" );
invokerTemplate . AddDefine ( "returnType" , "void" );
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
invokerTemplate . AddDefine ( "suitableType" , baseInterface . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , funcInvoking );
var backend = invokerTemplate . Compile ();
2025-03-25 21:27:54 +03:00
MethodRepoTemplate . Insert ( "invoker" , backend );
2025-03-10 00:00:24 +03:00
invokers . Add ( backend );
}
private void GeneratePropertyMethod ( IMethodSymbol method ,
List <( string , IMethodSymbol )> propsCollection , List < string > invokers , INamedTypeSymbol baseInterace )
2025-03-07 15:46:22 +03:00
{
2025-03-08 19:46:59 +03:00
var level = "\t\t\t" ;
2025-03-10 00:00:24 +03:00
var index = invokers . Count ;
2025-03-08 19:46:59 +03:00
string frontend ;
string backend = string . Empty ;
2025-03-07 15:46:22 +03:00
if ( method . MethodKind == MethodKind . PropertyGet )
{
var parametersArray = "" ;
if ( method . Parameters . Length != 0 )
{
parametersArray = $", new object[] {{{string.Join(" , ", method.Parameters.Select(p => p.Name))}}}" ;
2025-03-08 19:46:59 +03:00
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", method.Parameters.Select(p => " typeof ( " + p.Type.ToDisplayString() + " ) "))}" );
var parameterInserts = string . Join ( ", " ,
2025-03-10 00:00:24 +03:00
method . Parameters . Select (
p => Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" )));
2025-03-27 20:06:09 +03:00
var invokerTemplate = ( TemplateDocument ) MethodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "false" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToDisplayString ());
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , $"(i as {method.ContainingType.ToDisplayString()})[{parameterInserts}]" );
backend = invokerTemplate . Compile ();
// backend = $@"new mROA.Implementation.MethodInvoker
// {level}{{
// {level} IsVoid = false,
// {level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
// {level} ParameterTypes = new Type[] {{ {parameterTypes} }},
// {level} SuitableType = typeof({baseInterace.ToDisplayString()}),
// {level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}],
// {level}}}";
2025-03-08 19:46:59 +03:00
}
else
{
2025-03-27 20:06:09 +03:00
var invokerTemplate = ( TemplateDocument ) MethodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "false" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToDisplayString ());
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , $"(i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}" );
backend = invokerTemplate . Compile ();
// backend = $@"new mROA.Implementation.MethodInvoker
// {level}{{
// {level} IsVoid = false,
// {level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
// {level} SuitableType = typeof({baseInterace.ToDisplayString()}),
// {level} Invoking = (i, _, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name},
// {level}}}";
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
frontend =
2025-03-08 12:10:12 +03:00
$"get => GetResultAsync<{method.ReturnType.ToDisplayString()}>({index}{parametersArray}).GetAwaiter().GetResult();" ;
2025-03-07 15:46:22 +03:00
}
else
{
2025-03-08 12:10:12 +03:00
var parametersArray = "value" ;
2025-03-08 19:46:59 +03:00
if ( method . Parameters . Length != 1 )
2025-03-07 15:46:22 +03:00
{
2025-03-08 12:10:12 +03:00
parametersArray = string . Join ( ", " , method . Parameters . Select ( p => p . Name ));
2025-03-08 19:46:59 +03:00
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", method.Parameters.Select(p => $" typeof ({ p . Type . ToDisplayString ()}) "))}" );
var parameterInserts = string . Join ( ", " ,
2025-03-27 17:41:16 +03:00
method . Parameters . Take ( method . Parameters . Length - 1 ). Select ( p =>
Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" )));
2025-03-08 19:46:59 +03:00
var valueInsert = Caster ( method . Parameters . Last (). Type ,
"parameters[" + ( method . Parameters . Length - 1 ) + "]" );
2025-03-27 20:06:09 +03:00
var invokerTemplate = ( TemplateDocument ) MethodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "true" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToDisplayString ());
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , $"(i as {method.ContainingType.ToDisplayString()})[{parameterInserts}] = {valueInsert}" );
backend = invokerTemplate . Compile ();
// backend = $@"new mROA.Implementation.MethodInvoker
// {level}{{
// {level} IsVoid = true,
// {level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
// {level} ParameterTypes = new Type[] {{ {parameterTypes} }},
// {level} SuitableType = typeof({baseInterace.ToDisplayString()}),
// {level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}] = {valueInsert},
// {level}}}";
2025-03-08 19:46:59 +03:00
}
else
{
2025-03-27 20:06:09 +03:00
var invokerTemplate = ( TemplateDocument ) MethodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "true" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToDisplayString ());
invokerTemplate . AddDefine ( "parametersType" , $"typeof({method.Parameters.First().Type.ToDisplayString()})" );
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToDisplayString ());
invokerTemplate . AddDefine ( "funcInvoking" , $"(i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, " parameters [ 0 ] ")}" );
backend = invokerTemplate . Compile ();
// backend = $@"new mROA.Implementation.MethodInvoker
// {level}{{
// {level} IsVoid = true,
// {level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
// {level} ParameterTypes = new Type[] {{ typeof({method.Parameters.First().Type.ToDisplayString()}) }},
// {level} SuitableType = typeof({baseInterace.ToDisplayString()}),
// {level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, "parameters[0]")},
// {level}}}";
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
frontend = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();" ;
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
propsCollection . Add (( frontend , method ));
2025-03-25 21:27:54 +03:00
MethodRepoTemplate . Insert ( "invoker" , backend );
2025-03-08 19:46:59 +03:00
invokers . Add ( backend );
2025-03-07 15:46:22 +03:00
}
2025-02-20 16:10:09 +03:00
private static string ToFullString ( IParameterSymbol parameter )
=> /*parameter.Type.ContainingNamespace is null*/
/*?*/ parameter . ToDisplayString ();
/*: $"{parameter.Type.ContainingNamespace.ToDisplayString()}.{parameter.Type.MetadataName} {parameter.Name}";*/
private static string ToFullString ( ITypeSymbol type ) =>
// => type.ContainingNamespace is null || type.Name == "Void"
type . ToDisplayString ();
// : $"{type.ContainingNamespace.ToDisplayString()}.{type.MetadataName}";
private string ExtractTaskType ( ITypeSymbol taskType )
2025-02-06 09:18:52 +03:00
{
2025-03-27 17:41:16 +03:00
var generics = (( INamedTypeSymbol ) taskType ). TypeArguments ;
return generics . Length == 0 ? "void" : generics [ 0 ]. ToDisplayString ();
2025-02-20 16:10:09 +03:00
}
2025-02-05 00:26:28 +03:00
2025-02-20 16:10:09 +03:00
private bool ContainsSOIAttribute ( SyntaxList < AttributeListSyntax > attributes , GeneratorExecutionContext context ,
InterfaceDeclarationSyntax interfaceDeclarationSyntax )
{
2025-03-07 09:01:21 +03:00
foreach ( var attributeSyntax in
attributes . SelectMany ( attributeListSyntax => attributeListSyntax . Attributes ))
2025-02-20 16:10:09 +03:00
{
if ( context . Compilation . GetSemanticModel ( interfaceDeclarationSyntax . SyntaxTree )
. GetSymbolInfo ( attributeSyntax ). Symbol is not IMethodSymbol attributeSymbol )
continue ; // if we can't get the symbol, ignore it
2025-02-18 00:10:53 +03:00
2025-02-20 16:10:09 +03:00
string attributeName = attributeSymbol . ContainingType . ToDisplayString ();
2025-02-18 00:10:53 +03:00
2025-02-20 16:10:09 +03:00
// Check the full name of the [Report] attribute.
if ( attributeName == "mROA.Implementation.Attributes.SharedObjectInterfaceAttribute" )
return true ;
}
2025-02-18 00:10:53 +03:00
2025-02-20 16:10:09 +03:00
return false ;
}
2025-03-06 14:10:52 +03:00
2025-03-07 09:01:21 +03:00
private List < IMethodSymbol > CollectMembers ( INamedTypeSymbol type )
2025-03-06 14:10:52 +03:00
{
var methods = type . GetMembers (). OfType < IMethodSymbol >(). ToList ();
foreach ( var inner in type . AllInterfaces )
{
methods . AddRange ( inner . GetMembers (). OfType < IMethodSymbol >());
}
2025-03-07 09:01:21 +03:00
methods . RemoveAll ( m => m . Name == "Dispose" );
2025-03-06 14:10:52 +03:00
return methods . OrderBy ( i => i . Name ). ToList ();
}
2025-02-18 00:10:53 +03:00
}
2025-02-04 19:02:39 +03:00
}