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 ;
2025-07-14 21:18:36 +03:00
using System.Threading ;
2025-02-04 19:02:39 +03:00
using Microsoft.CodeAnalysis ;
using Microsoft.CodeAnalysis.CSharp.Syntax ;
using Microsoft.CodeAnalysis.Text ;
2025-07-15 16:37:06 +03:00
using mROA.Codegen.Templates ;
2025-03-29 19:31:10 +03:00
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-07-14 21:18:36 +03:00
private const string SharedObjectInterfaceAttributeName = "SharedObjectInterface" ;
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-15 16:37:06 +03:00
2025-07-10 18:58:49 +03:00
private int _currentInternalCallIndex ;
2025-03-29 19:31:10 +03:00
2025-07-14 19:11:54 +03:00
public void Initialize ( IncrementalGeneratorInitializationContext context )
{
var syntaxes = context . SyntaxProvider . CreateSyntaxProvider (
2025-07-14 21:18:36 +03:00
NodeIsInterfaceWithSharedObjectInterfaceAttribute ,
TransformToInterfaceDeclarationSyntax );
2025-07-14 19:13:19 +03:00
2025-07-14 21:18:36 +03:00
var incrementalValueProvider = context . CompilationProvider . Combine ( syntaxes . Collect ());
context . RegisterSourceOutput ( incrementalValueProvider ,
2025-07-14 19:13:19 +03:00
( productionContext , pair ) => GenerateCode ( productionContext , pair . Left , pair . Right ));
2025-07-14 19:11:54 +03:00
}
2025-07-14 19:13:19 +03:00
2025-07-14 21:18:36 +03:00
private static InterfaceDeclarationSyntax TransformToInterfaceDeclarationSyntax ( GeneratorSyntaxContext context ,
CancellationToken _ )
{
if ( context . Node is not InterfaceDeclarationSyntax interfaceSyntax )
throw new InvalidOperationException ();
return interfaceSyntax ;
}
private static bool NodeIsInterfaceWithSharedObjectInterfaceAttribute ( SyntaxNode node , CancellationToken _ )
{
if ( node is not InterfaceDeclarationSyntax interfaceSyntax )
return false ;
var attributes = interfaceSyntax . AttributeLists . SelectMany ( list => list . Attributes );
return attributes . Any ( attribute => attribute . ToFullString () == SharedObjectInterfaceAttributeName );
}
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-07-15 16:37:06 +03:00
var methodRepoTemplate = new MethodRepoTemplate ();
var typeBinder = new RemoteTypeBinderTemplate ();
var indexProviderTemplate = new IndexProviderTemplate ();
2025-03-29 19:31:10 +03:00
var totalMethods = new List < IMethodSymbol >();
2025-07-14 19:13:19 +03:00
2025-07-15 16:37:06 +03:00
if ( compilation . AssemblyName == null )
throw new InvalidOperationException ();
2025-07-03 17:37:25 +03:00
2025-07-15 16:37:06 +03:00
indexProviderTemplate . DefineNamespace ( 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-07-15 16:37:06 +03:00
var proxyTemplate = new ProxyTemplate ();
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-07-15 16:37:06 +03:00
GeneratePropertyMethod ( methodRepoTemplate , method , propertiesAccessMethods , invokers ,
2025-03-10 00:00:24 +03:00
classSymbol );
2025-03-06 14:10:52 +03:00
continue ;
2025-03-07 15:46:22 +03:00
default :
2025-07-15 16:37:06 +03:00
GenerateDeclaredMethod ( proxyTemplate , methodRepoTemplate , 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-07-15 16:37:06 +03:00
proxyTemplate . InsertMethods ( 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-07-15 16:37:06 +03:00
proxyTemplate . InsertMethods ( $"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-07-15 16:37:06 +03:00
GenerateEventImplementation ( proxyTemplate , methodRepoTemplate , typeBinder , classSymbol , invokers , context );
2025-07-03 17:37:25 +03:00
var endInvokers = invokers . Count ;
2025-07-15 16:37:06 +03:00
indexProviderTemplate . InsertIndexSpan ( $"{{ typeof({originalName}), new[] {{ {CodegenUtilities.JoinWithComa(Enumerable.Range(startInvokers, endInvokers - startInvokers).Select(i => i.ToString()))} }} }}," );
proxyTemplate . DefineClassName ( className );
proxyTemplate . DefineOriginalName ( originalName );
proxyTemplate . DefineNamespaceName ( namespaceName );
2025-03-26 14:43:06 +03:00
2025-07-15 16:37:06 +03:00
var code = proxyTemplate . 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-15 16:37:06 +03:00
indexProviderTemplate . InsertRemoteTypePair ( $"{{ 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-07-15 16:37:06 +03:00
var coCodegenRepoCode = methodRepoTemplate . Compile ();
indexProviderTemplate . DefineLevel ( apiLevel . ToString ());
indexProviderTemplate . DefineLen ( invokers . Count . ToString ());
2025-07-14 19:13:19 +03:00
2025-07-15 16:37:06 +03:00
var providerCode = indexProviderTemplate . Compile ();
2025-03-08 19:46:59 +03:00
#if ! DONT_ADD
2025-07-14 19:13:19 +03:00
context . AddSource ( "GeneratedInvokersCollection.g.cs" ,
SourceText . From ( coCodegenRepoCode , Encoding . UTF8 ));
2025-07-03 17:37:25 +03:00
context . AddSource ( "GeneratedIndexProvider.g.cs" , SourceText . From ( providerCode , Encoding . UTF8 ));
2025-07-14 19:13:19 +03:00
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-15 16:37:06 +03:00
if ( indexProviderTemplate . IsRemoteTypePairInserted ())
2025-02-20 16:10:09 +03:00
{
2025-07-15 16:37:06 +03:00
var frontendRepoCode = typeBinder . 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-07-15 16:37:06 +03:00
private void GenerateEventImplementation ( ProxyTemplate proxyTemplate , MethodRepoTemplate methodRepoTemplate , RemoteTypeBinderTemplate remoteTypeBinder , INamedTypeSymbol classSymbol ,
2025-07-14 21:18:36 +03:00
List < string > invokers , 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-07-15 16:37:06 +03:00
var partialInterfaceTemplate = new PartialInterfaceTemplate ();
partialInterfaceTemplate . DefineName ( classSymbol . Name );
partialInterfaceTemplate . DefineNamespace ( classSymbol . ContainingNamespace . ToDisplayString ());
var objectBinderTemplate = remoteTypeBinder . CloneInnerObjectBinder ();
2025-03-29 19:31:10 +03:00
foreach ( var currentEvent in events )
2025-03-10 00:00:24 +03:00
{
var additionalMethod = GenerateMethodExternalCaller ( currentEvent , out var signature );
2025-07-15 16:37:06 +03:00
proxyTemplate . InsertMethods ( additionalMethod );
2025-03-25 21:27:54 +03:00
// declaredMethods.Add(additionalMethod);
2025-07-15 16:37:06 +03:00
partialInterfaceTemplate . InsertSignature ( signature );
GenerateEventCode ( methodRepoTemplate , currentEvent , invokers , classSymbol );
GenerateBinderCode ( objectBinderTemplate , currentEvent , classSymbol );
2025-03-10 00:00:24 +03:00
}
2025-07-15 16:37:06 +03:00
objectBinderTemplate . DefineType ( classSymbol . ToUnityString ());
var partialInterface = partialInterfaceTemplate . Compile ();
2025-03-26 14:43:06 +03:00
var binder = objectBinderTemplate . Compile ();
2025-07-15 16:37:06 +03:00
remoteTypeBinder . InsertEventBinder ( 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
}
2025-07-14 20:01:40 +03:00
private static string GenerateMethodExternalCaller ( IEventSymbol eventSymbol , out string interfaceSignature )
2025-03-10 00:00:24 +03:00
{
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-15 16:37:06 +03:00
private void GenerateDeclaredMethod ( ProxyTemplate proxyTemplate , MethodRepoTemplate methodRepoTemplate , IMethodSymbol method , List < string > invokers ,
2025-07-14 21:18:36 +03:00
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-07-15 16:37:06 +03:00
proxyTemplate . InsertMethods ( 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-07-20 00:20:05 +03:00
parametersInsertList . Add ( "(RequestContext)special[0]" );
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-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerAsyncInvoker ();
invokerTemplate . DefineIsVoid ( isVoid . ToString (). ToLower ());
invokerTemplate . DefineReturnType ( CodegenUtilities . ExtractTaskType ( method . ReturnType ));
invokerTemplate . DefineParametersType ( parameterTypes );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( funcInvoking );
invokerTemplate . DefineIsTrusted ((! 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-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerSyncInvoker ();
invokerTemplate . DefineIsVoid ( isVoid . ToString (). ToLower ());
invokerTemplate . DefineReturnType ( isVoid ? "void" : method . ReturnType . ToUnityString ());
invokerTemplate . DefineParametersType ( parameterTypes );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( funcInvoking );
invokerTemplate . DefineIsTrusted ((! isUntrusted ). ToString (). ToLower ());
2025-03-27 17:41:16 +03:00
backend = invokerTemplate . Compile ();
}
2025-07-15 16:37:06 +03:00
methodRepoTemplate . InsertInvoke ( backend );
2025-03-08 19:46:59 +03:00
invokers . Add ( backend );
2025-03-07 15:46:22 +03:00
}
2025-07-15 16:37:06 +03:00
private void GenerateBinderCode ( ObjectBinderTemplate objectBinderTemplate , IEventSymbol eventSymbol , INamedTypeSymbol baseType )
2025-03-10 16:35:56 +03:00
{
2025-07-15 16:37:06 +03:00
var eventBinderTemplate = objectBinderTemplate . CloneInnerEventBinder ();
2025-03-27 17:41:16 +03:00
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-03-10 16:35:56 +03:00
var parametersDeclaration = string . Join ( ", " ,
2025-07-14 19:13:19 +03:00
CodegenUtilities . JoinWithComa ( Enumerable . Range ( 0 , parameters . Count ). Select ( i => "p" + i )));
2025-03-10 16:35:56 +03:00
2025-07-14 19:11:54 +03:00
var pi = 0 ;
2025-07-14 19:13:19 +03:00
var transferParameters = CodegenUtilities . JoinWithComa ( parameters . Select ( i => ( i , pi ++))
. Where ( i => ! ParameterFilterForType ( i . i ))
. Select ( i => "p" + i . Item2 ));
2025-07-15 16:37:06 +03:00
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-07-15 16:37:06 +03:00
eventBinderTemplate . DefineCallFilter ( callFilter );
2025-03-10 16:35:56 +03:00
}
2025-07-15 16:37:06 +03:00
eventBinderTemplate . DefineType ( baseType . ToUnityString ());
eventBinderTemplate . DefineEventName ( eventSymbol . Name );
eventBinderTemplate . DefineParametersDeclaration ( parametersDeclaration );
eventBinderTemplate . DefineCommandIdTag ( $"context.CallIndexProvider.GetIndices(typeof({baseType.ToUnityString()}))[{index}]" );
eventBinderTemplate . DefineTransferParameters ( transferParameters );
2025-03-26 14:43:06 +03:00
var eventBinderCode = eventBinderTemplate . Compile ();
2025-07-15 16:37:06 +03:00
objectBinderTemplate . InsertEventBinder ( eventBinderCode );
2025-03-10 16:35:56 +03:00
}
2025-07-15 16:37:06 +03:00
private void GenerateEventCode ( MethodRepoTemplate methodRepoTemplate , IEventSymbol eventSymbol , List < string > invokers , ITypeSymbol baseInterface )
2025-03-10 00:00:24 +03:00
{
var level = "\t\t\t" ;
2025-03-08 12:10:12 +03:00
2025-05-15 08:20:32 +03:00
int pi = 0 ;
2025-07-14 19:13:19 +03:00
var parameters = (( INamedTypeSymbol ) eventSymbol . Type ). TypeArguments . Select ( i => ( i , pi ++))
. ToImmutableArray ();
2025-05-15 08:20:32 +03:00
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-07-20 00:20:05 +03:00
parametersInsertList . Add ( "(RequestContext)special[0]" );
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
2025-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerSyncInvoker ();
invokerTemplate . DefineIsVoid ( "true" );
invokerTemplate . DefineReturnType ( "void" );
invokerTemplate . DefineParametersType ( parameterTypes );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( funcInvoking );
invokerTemplate . DefineIsTrusted ( "true" );
2025-03-27 20:06:09 +03:00
var backend = invokerTemplate . Compile ();
2025-07-15 16:37:06 +03:00
methodRepoTemplate . InsertInvoke ( backend );
2025-03-25 21:27:54 +03:00
2025-03-10 00:00:24 +03:00
invokers . Add ( backend );
}
2025-07-15 16:37:06 +03:00
private void GeneratePropertyMethod ( MethodRepoTemplate methodRepoTemplate , IMethodSymbol method ,
List <( string , IMethodSymbol )> propsCollection , List < string > invokers , INamedTypeSymbol baseInterface )
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-05-03 21:43:02 +03:00
method . Parameters . Select ( p =>
2025-07-14 19:13:19 +03:00
CodegenUtilities . Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" )));
2025-03-29 19:31:10 +03:00
2025-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerSyncInvoker ();
invokerTemplate . DefineIsVoid ( "false" );
invokerTemplate . DefineReturnType ( method . ReturnType . ToUnityString ());
invokerTemplate . DefineParametersType ( parameterTypes );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( $"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}]" );
invokerTemplate . DefineIsTrusted ( "true" );
2025-05-03 23:09:32 +03:00
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-08 19:46:59 +03:00
}
else
{
2025-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerSyncInvoker ();
invokerTemplate . DefineIsVoid ( "false" );
invokerTemplate . DefineReturnType ( method . ReturnType . ToUnityString ());
invokerTemplate . DefineParametersType ( "" );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( $"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name}" );
invokerTemplate . DefineIsTrusted ( "true" );
2025-05-03 23:09:32 +03:00
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-03-27 17:41:16 +03:00
method . Parameters . Take ( method . Parameters . Length - 1 ). Select ( p =>
2025-07-14 19:13:19 +03:00
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-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerSyncInvoker ();
invokerTemplate . DefineIsVoid ( "true" );
invokerTemplate . DefineReturnType ( method . ReturnType . ToUnityString ());
invokerTemplate . DefineParametersType ( parameterTypes );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( $"(i as {method.ContainingType.ToUnityString()})[{parameterInserts}] = {valueInsert}" );
invokerTemplate . DefineIsTrusted ( "true" );
2025-05-03 23:09:32 +03:00
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-08 19:46:59 +03:00
}
else
{
2025-07-15 16:37:06 +03:00
var invokerTemplate = methodRepoTemplate . CloneInnerSyncInvoker ();
invokerTemplate . DefineIsVoid ( "true" );
invokerTemplate . DefineReturnType ( method . ReturnType . ToUnityString ());
invokerTemplate . DefineParametersType ( $"typeof({method.Parameters.First().Type.ToUnityString()})" );
invokerTemplate . DefineSuitableType ( baseInterface . ToUnityString ());
invokerTemplate . DefineFuncInvoking ( $"(i as {method.ContainingType.ToUnityString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {CodegenUtilities.Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, " parameters [ 0 ] ")}" );
invokerTemplate . DefineIsTrusted ( "true" );
2025-05-03 23:09:32 +03:00
2025-03-27 20:06:09 +03:00
backend = invokerTemplate . Compile ();
2025-03-07 15:46:22 +03:00
}
2025-07-14 19:13:19 +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-07-15 16:37:06 +03:00
methodRepoTemplate . InsertInvoke ( 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
}
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 )
2025-03-29 19:31:10 +03:00
{
2025-07-14 19:11:54 +03:00
return $"{eventSymbol.Name}External" ;
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 Caster ( ITypeSymbol type , string inner )
2025-03-29 19:31:10 +03:00
{
2025-07-14 19:11:54 +03:00
if (! type . IsValueType )
return inner +
" as " +
type . ToUnityString ();
return $"({type.ToUnityString()})" + inner ;
2025-03-29 19:31:10 +03:00
}
2025-02-20 16:10:09 +03:00
2025-07-14 19:11:54 +03:00
public static string JoinWithComa ( IEnumerable < string > parts )
2025-02-06 09:18:52 +03:00
{
2025-07-14 19:11:54 +03:00
return string . Join ( ", " , parts );
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 string ToFullString ( IParameterSymbol parameter )
2025-02-20 16:10:09 +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-02-18 00:10:53 +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-18 00:10:53 +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-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-03-29 19:31:10 +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:13:19 +03:00
return parts . Any ( i => i . Kind == SymbolDisplayPartKind . Keyword )
? parts . ToUnityString ()
2025-07-14 19:11:54 +03:00
: 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
}