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 ;
using System.Linq ;
using System.Text ;
using Microsoft.CodeAnalysis ;
using Microsoft.CodeAnalysis.CSharp.Syntax ;
using Microsoft.CodeAnalysis.Text ;
2025-06-27 16:12:38 +03:00
using mROA.CodegenTools ;
using mROA.CodegenTools.Reading ;
2025-03-29 19:31:10 +03:00
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
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>
2025-03-29 19:31:10 +03:00
/// 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.
2025-02-20 16:10:09 +03:00
/// </summary>
[Generator]
2025-06-28 22:23:50 +03:00
public class mROAGenerator : IIncrementalGenerator
2025-02-20 16:10:09 +03:00
{
2025-03-29 19:31:10 +03:00
private static readonly Predicate < IParameterSymbol > ParameterFilter =
2025-03-10 00:00:24 +03:00
i => i . Type . Name is "CancellationToken" or "RequestContext" ;
2025-03-29 19:31:10 +03:00
private static readonly Predicate < ITypeSymbol > ParameterFilterForType =
2025-03-10 00:00:24 +03:00
i => i . Name is "CancellationToken" or "RequestContext" ;
2025-07-03 17:37:25 +03:00
private TemplateDocument _indexerTemplate ;
2025-03-29 19:31:10 +03:00
private TemplateDocument _binderTemplate ;
private TemplateDocument _classTemplate ;
private TemplateDocument _classTemplateOriginal ;
private TemplateDocument _interfaceTemplate ;
private TemplateDocument _interfaceTemplateOriginal ;
private TemplateDocument _methodInvokerOriginal ;
private TemplateDocument _methodRepoTemplate ;
2025-07-10 18:58:49 +03:00
private int _currentInternalCallIndex ;
2025-03-29 19:31:10 +03:00
2025-06-28 22:23:50 +03:00
2025-07-14 19:11:54 +03:00
public void Initialize ( IncrementalGeneratorInitializationContext context )
{
_methodRepoTemplate = TemplateReader . FromEmbeddedResource ( "MethodRepo.cstmpl" );
_methodInvokerOriginal =
(( InnerTemplateSection ) _methodRepoTemplate [ "syncInvoker" ]!). InnerTemplate ;
_classTemplateOriginal = TemplateReader . FromEmbeddedResource ( "Proxy.cstmpl" );
_binderTemplate = TemplateReader . FromEmbeddedResource ( "RemoteTypeBinder.cstmpl" );
_interfaceTemplateOriginal = TemplateReader . FromEmbeddedResource ( "PartialInterface.cstmpl" );
_indexerTemplate = TemplateReader . FromEmbeddedResource ( "IndexProvider.cstmpl" );
var syntaxes = context . SyntaxProvider . CreateSyntaxProvider (
( static ( node , _ ) => node is InterfaceDeclarationSyntax ), static ( node , _ ) => CodegenUtilities . ContainsSoiAttribute ( node )). Where ( i => i . usefull ). Select (( node , _ ) => node . node );
context . RegisterSourceOutput ( context . CompilationProvider . Combine ( syntaxes . Collect ()), ( productionContext , pair ) => GenerateCode ( productionContext , pair . Left , pair . Right ));
}
2025-06-28 22:23:50 +03:00
private void GenerateCode ( SourceProductionContext context , Compilation compilation ,
2025-02-20 16:10:09 +03:00
ImmutableArray < InterfaceDeclarationSyntax > classes )
{
2025-03-29 19:31:10 +03:00
var totalMethods = new List < IMethodSymbol >();
2025-07-03 17:37:25 +03:00
_indexerTemplate . AddDefine ( "namespace" , compilation . AssemblyName !);
2025-03-29 19:31:10 +03:00
var invokers = new List < string >();
2025-06-30 00:09:30 +03:00
var declarations = classes . ToList ();
2025-07-03 17:37:25 +03:00
var apiLevel = 0 ;
2025-02-20 16:10:09 +03:00
foreach ( var classDeclarationSyntax in declarations )
2025-03-07 09:01:21 +03:00
{
2025-07-03 17:37:25 +03:00
_currentInternalCallIndex = 0 ;
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-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-07-14 19:11:54 +03:00
var innerMethods = CodegenUtilities . 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-06-27 16:20:17 +03:00
className = className . TrimStart ( 'I' ) + "Proxy" ;
2025-03-06 14:10:52 +03:00
2025-03-29 19:31:10 +03:00
_classTemplate = ( TemplateDocument ) _classTemplateOriginal . Clone ();
2025-03-07 15:46:22 +03:00
var propertiesAccessMethods = new List <( string , IMethodSymbol )>();
2025-03-08 19:46:59 +03:00
2025-07-03 17:37:25 +03:00
int startInvokers = invokers . Count ;
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-29 19:31:10 +03:00
GenerateDeclaredMethod ( method , invokers , classSymbol );
2025-03-07 15:46:22 +03:00
break ;
2025-03-06 14:10:52 +03:00
}
2025-02-04 23:19:24 +03:00
2025-03-08 12:10:12 +03:00
foreach ( var symbol in associated )
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 =
2025-03-30 12:43:58 +03:00
$"public {propertySymbol.Type.ToUnityString()} this[{string.Join(" , ", propertySymbol.Parameters.Select(p => p.ToUnityString()))}] {{ {getter.Item1} {setter.Item1} }}" ;
2025-03-08 12:10:12 +03:00
else
impl =
2025-03-29 19:31:10 +03:00
$"public {propertySymbol.Type.ToUnityString()} {symbol.Name} {{ {getter.Item1} {setter.Item1} }}" ;
2025-03-26 14:43:06 +03:00
2025-03-29 19:31:10 +03:00
_classTemplate . Insert ( "methods" , impl );
2025-03-25 21:27:54 +03:00
// declaredMethods.Add(impl);
2025-03-08 12:10:12 +03:00
break ;
case IEventSymbol eventSymbol :
2025-03-29 19:31:10 +03:00
_classTemplate . Insert ( "methods" ,
$"public event {eventSymbol.Type.ToUnityString()}? {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-07-03 17:37:25 +03:00
var endInvokers = invokers . Count ;
2025-07-14 19:11:54 +03:00
_indexerTemplate . Insert ( "indexSpan" , $"{{ typeof({originalName}), new[] {{ {CodegenUtilities.JoinWithComa(Enumerable.Range(startInvokers, endInvokers - startInvokers).Select(i => i.ToString()))} }} }}," );
2025-03-29 19:31:10 +03:00
_classTemplate . AddDefine ( "className" , className );
_classTemplate . AddDefine ( "originalName" , originalName );
_classTemplate . AddDefine ( "namespaceName" , namespaceName );
2025-03-26 14:43:06 +03:00
2025-03-29 19:31:10 +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-07-03 17:37:25 +03:00
_indexerTemplate . Insert ( "remoteTypePair" ,
$"{{ typeof({classSymbol.ToUnityString()}), (id, r, c, indices) => new {namespaceName}.{className}(id, r, c, indices) }}" );
2025-02-20 16:10:09 +03:00
}
2025-03-08 22:31:40 +03:00
if ( totalMethods . Count != 0 )
2025-02-20 16:10:09 +03:00
{
2025-03-29 19:31:10 +03:00
var coCodegenRepoCode = _methodRepoTemplate . Compile ();
2025-07-03 17:37:25 +03:00
_indexerTemplate . AddDefine ( "level" , apiLevel . ToString ());
_indexerTemplate . AddDefine ( "len" , invokers . Count . ToString ());
var providerCode = _indexerTemplate . Compile ();
2025-03-08 19:46:59 +03:00
#if ! DONT_ADD
2025-07-03 17:37:25 +03:00
context . AddSource ( "GeneratedInvokersCollection.g.cs" , SourceText . From ( coCodegenRepoCode , Encoding . UTF8 ));
context . AddSource ( "GeneratedIndexProvider.g.cs" , SourceText . From ( providerCode , 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-07-03 17:37:25 +03:00
if ( _indexerTemplate [ "remoteTypePair+" ] != null )
2025-02-20 16:10:09 +03:00
{
2025-07-03 17:37:25 +03:00
var frontendRepoCode = _binderTemplate . Compile ();
2025-03-08 12:10:12 +03:00
#if ! DONT_ADD
2025-07-03 17:37:25 +03:00
context . AddSource ( "RemoteTypeBinder.g.cs" , SourceText . From ( frontendRepoCode , Encoding . UTF8 ));
2025-03-08 12:10:12 +03:00
#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 ,
2025-06-28 22:23:50 +03:00
SourceProductionContext 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-29 19:31:10 +03:00
_interfaceTemplate = ( TemplateDocument ) _interfaceTemplateOriginal . Clone ();
_interfaceTemplate . AddDefine ( "name" , classSymbol . Name );
_interfaceTemplate . AddDefine ( "namespace" , classSymbol . ContainingNamespace . ToDisplayString ());
2025-03-27 17:41:16 +03:00
var objectBinderTemplate =
2025-03-29 19:31:10 +03:00
( TemplateDocument )(( InnerTemplateSection ) _binderTemplate [ "objectBinderTemplate" ]!). InnerTemplate
. Clone ();
foreach ( var currentEvent in events )
2025-03-10 00:00:24 +03:00
{
var additionalMethod = GenerateMethodExternalCaller ( currentEvent , out var signature );
2025-03-29 19:31:10 +03:00
_classTemplate . Insert ( "methods" , additionalMethod );
2025-03-25 21:27:54 +03:00
// declaredMethods.Add(additionalMethod);
2025-03-29 19:31:10 +03:00
_interfaceTemplate . Insert ( "signature" , signature );
2025-03-10 00:00:24 +03:00
GenerateEventCode ( currentEvent , invokers , classSymbol );
2025-07-14 19:11:54 +03:00
GenerateBinderCode ( currentEvent , classSymbol , objectBinderTemplate );
2025-03-10 00:00:24 +03:00
}
2025-03-27 17:41:16 +03:00
2025-03-29 19:31:10 +03:00
objectBinderTemplate . AddDefine ( "type" , classSymbol . ToUnityString ());
var partialInterface = _interfaceTemplate . Compile ();
2025-03-26 14:43:06 +03:00
var binder = objectBinderTemplate . Compile ();
2025-03-29 19:31:10 +03:00
_binderTemplate . Insert ( "eventBinder" , binder );
2025-03-25 21:40:12 +03:00
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 )
{
2025-07-14 19:11:54 +03:00
const string level = "\t\t" ;
2025-03-29 19:31:10 +03:00
var parameters = (( INamedTypeSymbol ) eventSymbol . Type ). TypeArguments ;
2025-03-10 00:00:24 +03:00
var parameterIndex = 0 ;
var parametersDeclaration =
2025-03-29 19:31:10 +03:00
string . Join ( ", " , parameters . Select ( i => $"{i.ToUnityString()} p{parameterIndex++}" ));
2025-07-14 19:11:54 +03:00
var signature = $"public void {CodegenUtilities.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 ;
}
2025-07-14 19:11:54 +03:00
private void GenerateDeclaredMethod ( IMethodSymbol method , List < string > invokers , INamedTypeSymbol baseInterface )
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" ;
2025-03-29 19:31:10 +03:00
isVoid = ( isAsync && namedType . TypeParameters . Length == 0 ) || namedType . Name == "Void" ;
2025-03-07 15:46:22 +03:00
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 "
: " " ) +
2025-07-14 19:11:54 +03:00
$"{method.ReturnType.ToUnityString()} {method.Name}({string.Join(" , ", method.Parameters.Select(CodegenUtilities.ToFullString))}){{" );
2025-03-07 15:46:22 +03:00
2025-07-14 19:11:54 +03:00
var isUntrusted = method . GetAttributes (). Any ( i => i . AttributeClass ?. Name == "UntrustedAttribute" );
2025-05-03 21:43:02 +03:00
2025-03-07 15:46:22 +03:00
var prefix = isAsync ? "await " : "" ;
var postfix = ! isAsync ? isVoid ? ".Wait()" : ".GetAwaiter().GetResult()" : "" ;
var parameterLink = isParametrized
2025-03-29 19:31:10 +03:00
? ", new System.Object[] { " + string . Join ( ", " , parameters . Select ( i => i . Name )) + " }"
2025-03-07 15:46:22 +03:00
: string . Empty ;
2025-05-03 21:43:02 +03:00
string caller ;
if ( isUntrusted )
{
2025-07-10 18:58:49 +03:00
caller = $"CallUntrustedAsync(CallIndices[{_currentInternalCallIndex++}]{parameterLink})" ;
2025-05-03 21:43:02 +03:00
}
else
{
var tokenInsert = isAsync &&
method . Parameters . FirstOrDefault ( i => i . Type . Name == "CancellationToken" ) is
{ } tokenSymbol
? ", cancellationToken : " + tokenSymbol . Name
: string . Empty ;
caller = isVoid
2025-07-10 18:58:49 +03:00
? $"CallAsync(CallIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})"
2025-05-03 21:43:02 +03:00
: isAsync
2025-07-14 19:11:54 +03:00
? $"GetResultAsync<{CodegenUtilities.ExtractTaskType(method.ReturnType)}>(CallIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})"
: $"GetResultAsync<{CodegenUtilities.ToFullString(method.ReturnType)}>(CallIndices[{_currentInternalCallIndex++}]{parameterLink}{tokenInsert})" ;
2025-05-03 21:43:02 +03:00
if (! isVoid )
prefix = "return " + prefix ;
}
2025-03-07 15:46:22 +03:00
sb . AppendLine ( "\t\t\t" + prefix + caller + postfix + ";" );
sb . AppendLine ( "\t\t}" );
2025-03-29 19:31:10 +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 ( ", " ,
2025-03-29 19:31:10 +03:00
$"{string.Join(" , ", parameters.Select(p => $" typeof ({ p . Type . ToUnityString ()}) "))}" );
2025-03-08 19:46:59 +03:00
var level = "\t\t\t" ;
var parametersInsertList = new List < string >();
2025-03-29 19:31:10 +03:00
foreach ( var parameter in method . Parameters )
2025-03-08 19:46:59 +03:00
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 :
2025-07-14 19:11:54 +03:00
parametersInsertList . Add ( CodegenUtilities . Caster ( parameter . Type ,
2025-03-08 19:46:59 +03:00
$"parameters[{parameters.IndexOf(parameter)}]" ));
break ;
}
var parametersInsert = string . Join ( ", " , parametersInsertList );
2025-03-29 19:31:10 +03:00
string backend ;
2025-03-08 19:46:59 +03:00
2025-03-29 19:31:10 +03:00
string funcInvoking ;
2025-03-08 19:46:59 +03:00
if ( isAsync && ! isVoid )
funcInvoking =
2025-03-29 19:31:10 +03:00
$"(i as {method.ContainingType.ToUnityString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(t.Result); }})" ;
2025-03-08 19:46:59 +03:00
else if ( isAsync && isVoid )
funcInvoking =
2025-03-29 19:31:10 +03:00
$"(i as {method.ContainingType.ToUnityString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(null); }})" ;
2025-03-08 19:46:59 +03:00
else if (! isAsync && isVoid )
funcInvoking = $@"{{
2025-03-29 19:31:10 +03:00
{level} (i as {method.ContainingType.ToUnityString()}).{method.Name}({parametersInsert});
2025-03-08 19:46:59 +03:00
{level} return null;
{level} }}" ;
else
2025-03-29 19:31:10 +03:00
funcInvoking = $"(i as {method.ContainingType.ToUnityString()}).{method.Name}({parametersInsert})" ;
2025-03-08 19:46:59 +03:00
if ( isAsync )
2025-03-27 17:41:16 +03:00
{
2025-03-29 19:31:10 +03:00
var invokerTemplate =
( TemplateDocument )(( InnerTemplateSection ) _methodRepoTemplate [ "asyncInvoker" ]!). InnerTemplate
. Clone ();
2025-03-27 17:41:16 +03:00
invokerTemplate . AddDefine ( "isVoid" , isVoid . ToString (). ToLower ());
2025-07-14 19:11:54 +03:00
invokerTemplate . AddDefine ( "returnType" , CodegenUtilities . ExtractTaskType ( method . ReturnType ));
2025-03-27 17:41:16 +03:00
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
2025-07-14 19:11:54 +03:00
invokerTemplate . AddDefine ( "suitableType" , baseInterface . ToUnityString ());
2025-03-27 17:41:16 +03:00
invokerTemplate . AddDefine ( "funcInvoking" , funcInvoking );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , (! isUntrusted ). ToString (). ToLower ());
2025-03-27 17:41:16 +03:00
backend = invokerTemplate . Compile ();
}
2025-03-08 19:46:59 +03:00
else
2025-03-27 17:41:16 +03:00
{
2025-03-29 19:31:10 +03:00
var invokerTemplate = ( TemplateDocument ) _methodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , isVoid . ToString (). ToLower ());
invokerTemplate . AddDefine ( "returnType" , isVoid ? "void" : method . ReturnType . ToUnityString ());
2025-03-27 17:41:16 +03:00
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
2025-07-14 19:11:54 +03:00
invokerTemplate . AddDefine ( "suitableType" , baseInterface . ToUnityString ());
2025-03-27 17:41:16 +03:00
invokerTemplate . AddDefine ( "funcInvoking" , funcInvoking );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , (! isUntrusted ). ToString (). ToLower ());
2025-03-27 17:41:16 +03:00
backend = invokerTemplate . Compile ();
}
2025-03-29 19:31:10 +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-07-14 19:11:54 +03:00
private void GenerateBinderCode ( IEventSymbol eventSymbol , INamedTypeSymbol baseType , 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-07-03 17:42:27 +03:00
var index = _currentInternalCallIndex ;
2025-03-26 14:43:06 +03:00
var parameters = ( eventSymbol . Type as INamedTypeSymbol )!. TypeArguments . ToList ();
2025-07-14 19:11:54 +03:00
var parametersDeclaration = string . Join ( ", " , CodegenUtilities . JoinWithComa ( Enumerable . Range ( 0 , parameters . Count ). Select ( i => "p" + i )));
2025-03-10 16:35:56 +03:00
2025-05-15 08:20:32 +03:00
2025-07-14 19:11:54 +03:00
var pi = 0 ;
var transferParameters = CodegenUtilities . JoinWithComa ( parameters . Select ( i => ( i , pi ++)). Where ( i => ! ParameterFilterForType ( i . i ))
2025-05-15 08:20:32 +03:00
. Select ( i => "p" + i . Item2 ));
2025-03-10 16:35:56 +03:00
var requestIndex = parameters . FindIndex ( i => i . Name == "RequestContext" );
if ( requestIndex != - 1 )
{
2025-03-29 19:31:10 +03:00
var 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-29 19:31:10 +03:00
eventBinderTemplate . AddDefine ( "type" , baseType . ToUnityString ());
2025-03-26 14:43:06 +03:00
eventBinderTemplate . AddDefine ( "eventName" , eventSymbol . Name );
eventBinderTemplate . AddDefine ( "parametersDeclaration" , parametersDeclaration );
2025-07-03 17:37:25 +03:00
eventBinderTemplate . AddDefine ( "commandId" , $"context.CallIndexProvider.GetIndices(typeof({baseType.ToUnityString()}))[{index}]" );
2025-03-26 14:43:06 +03:00
eventBinderTemplate . AddDefine ( "transferParameters" , transferParameters );
var eventBinderCode = eventBinderTemplate . Compile ();
document . Insert ( "eventBinder" , eventBinderCode );
2025-03-10 16:35:56 +03:00
}
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-05-15 08:20:32 +03:00
int pi = 0 ;
var parameters = (( INamedTypeSymbol ) eventSymbol . Type ). TypeArguments . Select ( i => ( i , pi ++)). ToImmutableArray ();
var parsingParameters = parameters . RemoveAll ( i => ParameterFilterForType ( i . i )). ToList ();
2025-03-10 00:00:24 +03:00
var parameterTypes = string . Join ( ", " ,
2025-05-15 08:20:32 +03:00
$"{string.Join(" , ", parsingParameters.Select(p => $" typeof ({ p . i . ToUnityString ()}) "))}" );
2025-03-10 00:00:24 +03:00
var parametersInsertList = new List < string >();
2025-03-29 19:31:10 +03:00
foreach ( var parameter in parameters )
2025-05-15 08:20:32 +03:00
switch ( parameter . i . Name )
2025-03-10 00:00:24 +03:00
{
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 :
2025-07-14 19:11:54 +03:00
parametersInsertList . Add ( CodegenUtilities . Caster ( parameter . i ,
2025-05-15 08:20:32 +03:00
$"parameters[{parameter.Item2}]" ));
2025-03-10 00:00:24 +03:00
break ;
}
2025-03-29 19:31:10 +03:00
2025-03-10 00:00:24 +03:00
var parametersInsert = string . Join ( ", " , parametersInsertList );
2025-03-29 19:31:10 +03:00
2025-03-27 20:06:09 +03:00
var funcInvoking = $@"{{
2025-07-14 19:11:54 +03:00
{level} (i as {baseInterface.ToUnityString()}).{CodegenUtilities.EventExternalName(eventSymbol)}({parametersInsert});
2025-03-10 00:00:24 +03:00
{level} return null;
2025-03-27 20:06:09 +03:00
{level} }}" ;
2025-03-29 19:31:10 +03:00
var invokerTemplate = ( TemplateDocument ) _methodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "true" );
2025-03-27 20:06:09 +03:00
invokerTemplate . AddDefine ( "returnType" , "void" );
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
2025-03-29 19:31:10 +03:00
invokerTemplate . AddDefine ( "suitableType" , baseInterface . ToUnityString ());
2025-03-27 20:06:09 +03:00
invokerTemplate . AddDefine ( "funcInvoking" , funcInvoking );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , "true" );
2025-03-27 20:06:09 +03:00
var backend = invokerTemplate . Compile ();
2025-03-29 19:31:10 +03:00
_methodRepoTemplate . Insert ( "invoker" , backend );
2025-03-25 21:27:54 +03:00
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
string frontend ;
2025-03-29 19:31:10 +03:00
string backend ;
2025-03-07 15:46:22 +03:00
if ( method . MethodKind == MethodKind . PropertyGet )
{
var parametersArray = "" ;
if ( method . Parameters . Length != 0 )
{
2025-03-29 19:31:10 +03:00
parametersArray =
$", new System.Object[] {{ {string.Join(" , ", method.Parameters.Select(p => p.Name))} }}" ;
2025-03-08 19:46:59 +03:00
var parameterTypes = string . Join ( ", " ,
2025-03-29 19:31:10 +03:00
$"{string.Join(" , ", method.Parameters.Select(p => " typeof ( " + p.Type.ToUnityString() + " ) "))}" );
2025-03-08 19:46:59 +03:00
var parameterInserts = string . Join ( ", " ,
2025-07-14 19:11:54 +03:00
method . Parameters . Select ( p => CodegenUtilities . Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" )));
2025-03-29 19:31:10 +03:00
var invokerTemplate = ( TemplateDocument ) _methodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "false" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToUnityString ());
2025-03-27 20:06:09 +03:00
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
2025-03-29 19:31:10 +03:00
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToUnityString ());
invokerTemplate . AddDefine ( "funcInvoking" ,
$"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]" );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , "true" );
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-08 19:46:59 +03:00
}
else
{
2025-03-29 19:31:10 +03:00
var invokerTemplate = ( TemplateDocument ) _methodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "false" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToUnityString ());
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToUnityString ());
invokerTemplate . AddDefine ( "funcInvoking" ,
$"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}" );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , "true" );
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
frontend =
2025-07-10 18:58:49 +03:00
$"get => GetResultAsync<{method.ReturnType.ToUnityString()}>(CallIndices[{_currentInternalCallIndex++}]{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 ( ", " ,
2025-03-29 19:31:10 +03:00
$"{string.Join(" , ", method.Parameters.Select(p => $" typeof ({ p . Type . ToUnityString ()}) "))}" );
2025-03-08 19:46:59 +03:00
var parameterInserts = string . Join ( ", " ,
2025-07-14 19:11:54 +03:00
method . Parameters . Take ( method . Parameters . Length - 1 ). Select ( p => CodegenUtilities . Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" )));
2025-03-08 19:46:59 +03:00
2025-07-14 19:11:54 +03:00
var valueInsert = CodegenUtilities . Caster ( method . Parameters . Last (). Type ,
2025-03-08 19:46:59 +03:00
"parameters[" + ( method . Parameters . Length - 1 ) + "]" );
2025-03-29 19:31:10 +03:00
var invokerTemplate = ( TemplateDocument ) _methodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "true" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToUnityString ());
2025-03-27 20:06:09 +03:00
invokerTemplate . AddDefine ( "parametersType" , parameterTypes );
2025-03-29 19:31:10 +03:00
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToUnityString ());
invokerTemplate . AddDefine ( "funcInvoking" ,
$"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}] = {valueInsert}" );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , "true" );
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-08 19:46:59 +03:00
}
else
{
2025-03-29 19:31:10 +03:00
var invokerTemplate = ( TemplateDocument ) _methodInvokerOriginal . Clone ();
invokerTemplate . AddDefine ( "isVoid" , "true" );
invokerTemplate . AddDefine ( "returnType" , method . ReturnType . ToUnityString ());
invokerTemplate . AddDefine ( "parametersType" ,
$"typeof({method.Parameters.First().Type.ToUnityString()})" );
invokerTemplate . AddDefine ( "suitableType" , baseInterace . ToUnityString ());
invokerTemplate . AddDefine ( "funcInvoking" ,
2025-07-14 19:11:54 +03:00
$"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {CodegenUtilities.Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, " parameters [ 0 ] ")}" );
2025-05-03 23:09:32 +03:00
invokerTemplate . AddDefine ( "isTrusted" , "true" );
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-07 15:46:22 +03:00
}
2025-07-10 18:58:49 +03:00
frontend = $"set => CallAsync(CallIndices[{_currentInternalCallIndex++}], new System.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-29 19:31:10 +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-07-14 19:11:54 +03:00
}
public static class CodegenUtilities
{
public static string EventExternalName ( IEventSymbol eventSymbol )
{
return $"{eventSymbol.Name}External" ;
}
2025-03-07 15:46:22 +03:00
2025-07-14 19:11:54 +03:00
public static string Caster ( ITypeSymbol type , string inner )
{
if (! type . IsValueType )
return inner +
" as " +
type . ToUnityString ();
return $"({type.ToUnityString()})" + inner ;
}
public static string JoinWithComa ( IEnumerable < string > parts )
{
return string . Join ( ", " , parts );
}
public static string ToFullString ( IParameterSymbol parameter )
2025-03-29 19:31:10 +03:00
{
2025-03-30 12:43:58 +03:00
return parameter . Type . ToUnityString () + " " + parameter . Name ;
2025-03-29 19:31:10 +03:00
}
2025-03-31 10:08:20 +03:00
2025-07-14 19:11:54 +03:00
public static string ToFullString ( ITypeSymbol type )
2025-03-29 19:31:10 +03:00
{
return type . ToUnityString ();
}
2025-02-20 16:10:09 +03:00
2025-07-14 19:11:54 +03:00
public static 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 ;
2025-03-29 19:31:10 +03:00
return generics . Length == 0 ? "void" : generics [ 0 ]. ToUnityString ();
2025-02-20 16:10:09 +03:00
}
2025-02-05 00:26:28 +03:00
2025-07-14 19:11:54 +03:00
public static ( InterfaceDeclarationSyntax node , bool usefull ) ContainsSoiAttribute (
2025-06-28 22:23:50 +03:00
GeneratorSyntaxContext context )
2025-02-20 16:10:09 +03:00
{
2025-06-28 22:23:50 +03:00
var ids = ( InterfaceDeclarationSyntax ) context . Node ;
2025-07-14 19:11:54 +03:00
2025-06-28 22:23:50 +03:00
// Go through all attributes of the class.
2025-07-14 19:11:54 +03:00
foreach ( var attributeSyntax in ids . AttributeLists . SelectMany ( attributeListSyntax => attributeListSyntax . Attributes ))
2025-02-20 16:10:09 +03:00
{
2025-06-28 22:23:50 +03:00
if ( context . SemanticModel . GetSymbolInfo ( attributeSyntax ). Symbol is not IMethodSymbol attributeSymbol )
2025-02-20 16:10:09 +03:00
continue ; // if we can't get the symbol, ignore it
2025-02-18 00:10:53 +03:00
2025-07-14 19:11:54 +03:00
var 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.
2025-06-28 22:23:50 +03:00
if ( attributeName == $"mROA.Implementation.Attributes.SharedObjectInterfaceAttribute" )
return ( ids , true );
2025-02-20 16:10:09 +03:00
}
2025-02-18 00:10:53 +03:00
2025-06-28 22:23:50 +03:00
return ( ids , false );
2025-02-20 16:10:09 +03:00
}
2025-03-06 14:10:52 +03:00
2025-07-14 19:11:54 +03:00
public static List < IMethodSymbol > CollectMembers ( INamedTypeSymbol type )
2025-03-06 14:10:52 +03:00
{
var methods = type . GetMembers (). OfType < IMethodSymbol >(). ToList ();
2025-03-29 19:31:10 +03:00
foreach ( var inner in type . AllInterfaces ) methods . AddRange ( inner . GetMembers (). OfType < IMethodSymbol >());
2025-03-06 14:10:52 +03:00
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-07-14 19:11:54 +03:00
public static class CodegenExtensions
2025-03-29 19:31:10 +03:00
{
public static string ToUnityString ( this ITypeSymbol type )
{
var parts = type . ToDisplayParts ();
2025-07-14 19:11:54 +03:00
return parts . Any ( i => i . Kind == SymbolDisplayPartKind . Keyword )
? parts . ToUnityString ()
: type . ToDisplayString ();
2025-03-29 19:31:10 +03:00
}
2025-03-30 12:43:58 +03:00
public static string ToUnityString ( this IParameterSymbol parameter )
{
return parameter . Type . ToUnityString () + " " + parameter . Name ;
}
2025-03-31 10:08:20 +03:00
2025-07-14 19:11:54 +03:00
private static string ToUnityString ( this ImmutableArray < SymbolDisplayPart > parts )
2025-03-29 19:31:10 +03:00
{
var sb = new StringBuilder ();
foreach ( var displayPart in parts )
if ( displayPart . Kind == SymbolDisplayPartKind . Keyword )
{
if ( displayPart . ToString () == "void" )
{
sb . Append ( displayPart . ToString ());
continue ;
}
sb . Append ( $"{displayPart.Symbol!.ContainingNamespace.ToDisplayString()}.{displayPart.Symbol.Name}" );
}
else
{
sb . Append ( displayPart . ToString ());
}
return sb . ToString ();
}
}
2025-02-04 19:02:39 +03:00
}