Skip to content

Commit

Permalink
Created ActorProxyInvokeMethodAsync SyntaxFactoryHelper
Browse files Browse the repository at this point in the history
Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>
  • Loading branch information
m3nax committed Aug 9, 2024
1 parent b6d5bfa commit e002cee
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 74 deletions.
87 changes: 21 additions & 66 deletions src/Dapr.Actors.Generators/ActorClientGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ static void GenerateActorClientCode(SourceProductionContext context, ActorClient
})))
.WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[]
{
SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax("actorProxy"),
SyntaxFactoryHelpers.ThrowIfArgumentNull("actorProxy"),
SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
SyntaxFactory.MemberAccessExpression(
Expand Down Expand Up @@ -228,7 +228,7 @@ private static MethodDeclarationSyntax GenerateMethodImplementation(
var attributeData = method.GetAttributes()
.SingleOrDefault(a => a.AttributeClass?.Equals(generateActorClientAttributeSymbol, SymbolEqualityComparer.Default) == true);

// Generate the method name to use for the Dapr actor method invocation, using the Name the property of ActorMethodAttribute if specified,
// Generate the method name to use for the Dapr actor method invocation, using the Name property of ActorMethodAttribute if specified,
// or the original method name otherwise.
var daprMethodName = attributeData?.NamedArguments.SingleOrDefault(kvp => kvp.Key == "Name").Value.Value?.ToString() ?? method.Name;

Expand Down Expand Up @@ -262,71 +262,26 @@ private static MethodDeclarationSyntax GenerateMethodImplementation(
}
}

// Extract the return type of the method.
var methodReturnType = (INamedTypeSymbol)method.ReturnType;

// Define the type arguments to pass to the actor proxy method invocation.
var proxyInvocationTypeArguments = new List<TypeSyntax>()
.Concat(method.Parameters
.Where(p => p.Type is not INamedTypeSymbol { Name: "CancellationToken" })
.Select(p => SyntaxFactory.ParseTypeName(p.Type.ToString())))
.Concat(methodReturnType.TypeArguments
.Cast<INamedTypeSymbol>()
.Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())));

// Define the arguments to pass to the actor proxy method invocation.
var proxyInvocationArguments = new List<ArgumentSyntax>()
// Name of remote method to invoke.
.Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(daprMethodName))))
// Actor method arguments, including the CancellationToken if it exists.
.Concat(method.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))));

if (proxyInvocationTypeArguments.Any())
{
var generatedMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(method.ReturnType.ToString()), method.Name)
.WithModifiers(SyntaxFactory.TokenList(methodModifiers))
.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(methodParameters)))
.WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[]
{
SyntaxFactory.ReturnStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.ThisExpression(),
SyntaxFactory.IdentifierName("actorProxy")),
SyntaxFactory.GenericName(
SyntaxFactory.Identifier("InvokeMethodAsync"),
SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(proxyInvocationTypeArguments)))
))
.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments)))
),
})));

return generatedMethod;
}
else
{
var generatedMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(method.ReturnType.ToString()), method.Name)
.WithModifiers(SyntaxFactory.TokenList(methodModifiers))
.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(methodParameters)))
.WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[]
{
SyntaxFactory.ReturnStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.ThisExpression(),
SyntaxFactory.IdentifierName("actorProxy")),
SyntaxFactory.IdentifierName("InvokeMethodAsync")
))
.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments)))
),
})));

return generatedMethod;
}
// Generate the method implementation.
var generatedMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(method.ReturnType.ToString()), method.Name)
.WithModifiers(SyntaxFactory.TokenList(methodModifiers))
.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(methodParameters)))
.WithBody(SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[]
{
SyntaxFactory.ReturnStatement(SyntaxFactoryHelpers.ActorProxyInvokeMethodAsync(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.ThisExpression(),
SyntaxFactory.IdentifierName("actorProxy")),
daprMethodName,
method.Parameters,
methodReturnType.TypeArguments
)),
})));

return generatedMethod;
}
}
57 changes: 53 additions & 4 deletions src/Dapr.Actors.Generators/Helpers/SyntaxFactoryHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
using Dapr.Actors.Generators.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

Expand All @@ -14,7 +15,7 @@ public static partial class SyntaxFactoryHelpers
/// </summary>
/// <param name="argumentName"></param>
/// <returns></returns>
public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argumentName)
public static ThrowExpressionSyntax ThrowArgumentNullException(string argumentName)
{
return SyntaxFactory.ThrowExpression(
SyntaxFactory.Token(SyntaxKind.ThrowKeyword),
Expand All @@ -35,7 +36,7 @@ public static ThrowExpressionSyntax ThrowArgumentNullExceptionSyntax(string argu
/// </summary>
/// <param name="argumentName"></param>
/// <returns></returns>
public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName)
public static IfStatementSyntax ThrowIfArgumentNull(string argumentName)
{
return SyntaxFactory.IfStatement(
SyntaxFactory.BinaryExpression(
Expand All @@ -45,7 +46,7 @@ public static IfStatementSyntax ThrowIfArgumentNullSyntax(string argumentName)
),
SyntaxFactory.Block(SyntaxFactory.List(new StatementSyntax[]
{
SyntaxFactory.ExpressionStatement(ThrowArgumentNullExceptionSyntax(argumentName))
SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(argumentName))
}))
);
}
Expand Down Expand Up @@ -73,6 +74,54 @@ public static ExpressionSyntax NameOfExpression(string argumentName)
);
}

/// <summary>
/// Generates the invocation syntax to call a remote method with the actor proxy.
/// </summary>
/// <param name="actorProxyMemberSyntax">Memeber syntax to access actorProxy member.</param>
/// <param name="remoteMethodName">Name of remote method to invoke.</param>
/// <param name="remoteMethodParameters">Remote method parameters.</param>
/// <param name="remoteMethodReturnTypes">Return types of remote method invocation.</param>
/// <returns></returns>
public static InvocationExpressionSyntax ActorProxyInvokeMethodAsync(
MemberAccessExpressionSyntax actorProxyMemberSyntax,
string remoteMethodName,
IEnumerable<IParameterSymbol> remoteMethodParameters,
IEnumerable<ITypeSymbol> remoteMethodReturnTypes)
{
// Define the type arguments to pass to the actor proxy method invocation.
var proxyInvocationTypeArguments = new List<TypeSyntax>()
.Concat(remoteMethodParameters
.Where(p => p.Type is not { Name: "CancellationToken" })
.Select(p => SyntaxFactory.ParseTypeName(p.Type.ToString())))
.Concat(remoteMethodReturnTypes
.Select(a => SyntaxFactory.ParseTypeName(a.OriginalDefinition.ToString())));

// Define the arguments to pass to the actor proxy method invocation.
var proxyInvocationArguments = new List<ArgumentSyntax>()
// Name of remote method to invoke.
.Concat(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(remoteMethodName))))
// Actor method arguments, including the CancellationToken if it exists.
.Concat(remoteMethodParameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))));

// If the invocation has return types or input parameters, we need to use the generic version of the method.
SimpleNameSyntax invokeAsyncSyntax = proxyInvocationTypeArguments.Any()
? SyntaxFactory.GenericName(
SyntaxFactory.Identifier("InvokeMethodAsync"),
SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(proxyInvocationTypeArguments)))
: SyntaxFactory.IdentifierName("InvokeMethodAsync");

// Generate the invocation syntax.
var generatedInvocationSyntax = SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
actorProxyMemberSyntax,
invokeAsyncSyntax
))
.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(proxyInvocationArguments)));

return generatedInvocationSyntax;
}

/// <summary>
/// Returns the syntax kinds for the specified accessibility.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Dapr.Actors.Generators.Test.Helpers
public class SyntaxFactoryHelpersTests
{
[Fact]
public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName()
public void ThrowArgumentNullException_GenerateThrowArgumentNullExceptionSyntaxWithGivenArgumentName()
{
// Arrange
var argumentName = "arg0";
Expand All @@ -18,7 +18,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS
.ToFullString();

// Act
var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullExceptionSyntax(argumentName))
var generatedSource = SyntaxFactory.ExpressionStatement(SyntaxFactoryHelpers.ThrowArgumentNullException(argumentName))
.SyntaxTree
.GetRoot()
.NormalizeWhitespace()
Expand All @@ -29,7 +29,7 @@ public void ThrowArgumentNullExceptionSyntax_GenerateThrowArgumentNullExceptionS
}

[Fact]
public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgumentName()
public void ThrowIfArgumentNullException_GivesNullCheckSyntaxWithGivenArgumentName()
{
// Arrange
var argumentName = "arg0";
Expand All @@ -43,7 +43,7 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu
.ToFullString();

// Act
var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNullSyntax(argumentName)
var generatedSource = SyntaxFactoryHelpers.ThrowIfArgumentNull(argumentName)
.SyntaxTree
.GetRoot()
.NormalizeWhitespace()
Expand All @@ -53,6 +53,39 @@ public void ThrowIfArgumentNullExceptionSyntax_GivesNullCheckSyntaxWithGivenArgu
Assert.Equal(expectedSourceNormalized, generatedSource);
}

[Fact]
public void ActorProxyInvokeMethodAsync_WithoutReturnTypeAndParamters_ReturnNonGenericInvokeMethodAsync()
{
// Arrange
var remoteMethodName = "RemoteMethodToCall";
var remoteMethodParameters = Array.Empty<IParameterSymbol>();
var remoteMethodReturnTypes = Array.Empty<ITypeSymbol>();
var actorProxMemberAccessSyntax = SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.ThisExpression(),
SyntaxFactory.IdentifierName("actorProxy")
);
var expectedSource = $@"this.actorProxy.InvokeMethodAsync(""RemoteMethodToCall"")";
var expectedSourceNormalized = SyntaxFactory.ParseSyntaxTree(expectedSource)
.GetRoot()
.NormalizeWhitespace()
.ToFullString();

// Act
var generatedSource = SyntaxFactoryHelpers.ActorProxyInvokeMethodAsync(
actorProxMemberAccessSyntax,
remoteMethodName,
remoteMethodParameters,
remoteMethodReturnTypes)
.SyntaxTree
.GetRoot()
.NormalizeWhitespace()
.ToFullString(); ;

// Assert
Assert.Equal(expectedSourceNormalized, generatedSource);
}

[Fact]
public void NameOfExpression()
{
Expand Down

0 comments on commit e002cee

Please sign in to comment.