Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions src/Orleans.CodeGenerator/ActivatorGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Orleans.CodeGenerator.SyntaxGeneration;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Orleans.CodeGenerator.SyntaxGeneration;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Orleans.CodeGenerator;
Expand All @@ -14,6 +14,7 @@ private struct ConstructorArgument
public TypeSyntax Type { get; set; }
public string FieldName { get; set; }
public string ParameterName { get; set; }
public bool IsInvokablePool { get; set; }
}

public ClassDeclarationSyntax GenerateActivator(ISerializableTypeDescription type)
Expand All @@ -28,7 +29,14 @@ public ClassDeclarationSyntax GenerateActivator(ISerializableTypeDescription typ
{
foreach (var arg in parameters)
{
orderedFields.Add(new ConstructorArgument { Type = arg, FieldName = $"_arg{index}", ParameterName = $"arg{index}" });
orderedFields.Add(new ConstructorArgument
{
Type = arg,
FieldName = $"_arg{index}",
ParameterName = $"arg{index}",
IsInvokablePool = type is GeneratedInvokableDescription { UsesInvokablePool: true }
&& index == 0,
});
index++;
}
}
Expand Down Expand Up @@ -90,11 +98,14 @@ private static ConstructorDeclarationSyntax GenerateConstructor(
{
parameters.Add(Parameter(field.ParameterName.ToIdentifier()).WithType(field.Type));

var value = field.IsInvokablePool
? field.ParameterName.ToIdentifierName()
: Unwrapped(field.ParameterName.ToIdentifierName());
body.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
field.FieldName.ToIdentifierName(),
Unwrapped(field.ParameterName.ToIdentifierName()))));
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
field.FieldName.ToIdentifierName(),
value)));
}

var constructorDeclaration = ConstructorDeclaration(simpleClassName)
Expand All @@ -114,6 +125,32 @@ static ExpressionSyntax Unwrapped(ExpressionSyntax expr)

private static MemberDeclarationSyntax GenerateCreateMethod(ISerializableTypeDescription type, List<ConstructorArgument> orderedFields)
{
foreach (var field in orderedFields)
{
if (field.IsInvokablePool)
{
var arguments = orderedFields.Select(static field => Argument(field.FieldName.ToIdentifierName()));
var pooledCreateObject = ObjectCreationExpression(type.TypeSyntax)
.WithArgumentList(ArgumentList(SeparatedList(arguments)));
var tryGet = InvocationExpression(
field.FieldName.ToIdentifierName().Member("TryGet"),
ArgumentList(
SingletonSeparatedList(
Argument(
DeclarationExpression(
IdentifierName("var"),
SingleVariableDesignation(Identifier("item"))))
.WithRefKindKeyword(Token(SyntaxKind.OutKeyword)))));

return MethodDeclaration(type.TypeSyntax, "Create")
.WithExpressionBody(
ArrowExpressionClause(
ConditionalExpression(tryGet, IdentifierName("item"), pooledCreateObject)))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
.AddModifiers(Token(SyntaxKind.PublicKeyword));
}
}

ExpressionSyntax createObject;
if (type.ActivatorConstructorParameters is { Count: > 0 })
{
Expand All @@ -135,4 +172,5 @@ private static MemberDeclarationSyntax GenerateCreateMethod(ISerializableTypeDes
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
.AddModifiers(Token(SyntaxKind.PublicKeyword));
}

}
123 changes: 115 additions & 8 deletions src/Orleans.CodeGenerator/InvokableGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Orleans.CodeGenerator.SyntaxGeneration;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Orleans.CodeGenerator.Diagnostics;
using Orleans.CodeGenerator.SyntaxGeneration;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Orleans.CodeGenerator;
Expand All @@ -22,9 +22,24 @@ public GeneratedInvokableDescription Generate(InvokableMethodDescription invokab
var generatedClassName = GetSimpleClassName(invokableMethodInfo);

var baseClassType = GetBaseClassType(invokableMethodInfo);
var fieldDescriptions = GetFieldDescriptions(invokableMethodInfo);
var fields = GetFieldDeclarations(invokableMethodInfo, fieldDescriptions);
var (ctor, ctorArgs) = GenerateConstructor(generatedClassName, invokableMethodInfo, baseClassType);
var fieldDescriptions = GetFieldDescriptions(invokableMethodInfo, baseClassType);
var invokableTypeSyntax = CreateInvokableTypeSyntax(generatedClassName, invokableMethodInfo);
var fields = GetFieldDeclarations(invokableMethodInfo, fieldDescriptions, invokableTypeSyntax);
var (ctor, ctorArgs) = GenerateConstructor(generatedClassName, invokableMethodInfo, baseClassType, fieldDescriptions, invokableTypeSyntax);
var compatibilityCtor = fieldDescriptions.OfType<PoolFieldDescription>().Any()
? ConstructorDeclaration(generatedClassName)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.WithInitializer(
ConstructorInitializer(
SyntaxKind.ThisConstructorInitializer,
ArgumentList(
SingletonSeparatedList(
Argument(
PostfixUnaryExpression(
SyntaxKind.SuppressNullableWarningExpression,
LiteralExpression(SyntaxKind.NullLiteralExpression)))))))
.WithBody(Block())
: null;
var accessibility = GetAccessibility(method);
var compoundTypeAliases = GetCompoundTypeAliasAttributeArguments(invokableMethodInfo, invokableMethodInfo.Key);

Expand Down Expand Up @@ -52,6 +67,7 @@ public GeneratedInvokableDescription Generate(InvokableMethodDescription invokab
baseClassType,
fieldDescriptions,
fields,
compatibilityCtor,
ctor,
compoundTypeAliases,
targetField,
Expand All @@ -77,6 +93,7 @@ [.. fieldDescriptions.OfType<IMemberDescription>()],
serializationHooks,
baseClassType,
ctorArgs,
fieldDescriptions.OfType<PoolFieldDescription>().Any(),
compoundTypeAliases,
returnValueInitializerMethod,
classDeclaration);
Expand Down Expand Up @@ -106,6 +123,7 @@ private ClassDeclarationSyntax GetClassDeclarationSyntax(
INamedTypeSymbol baseClassType,
List<InvokerFieldDescription> fieldDescriptions,
MemberDeclarationSyntax[] fields,
ConstructorDeclarationSyntax? compatibilityCtor,
ConstructorDeclarationSyntax? ctor,
List<CompoundTypeAliasComponent[]> compoundTypeAliases,
TargetFieldDescription targetField,
Expand All @@ -123,7 +141,12 @@ private ClassDeclarationSyntax GetClassDeclarationSyntax(
AttributeList(SingletonSeparatedList(GetCompoundTypeAliasAttribute(alias))));
}

if (ctor != null)
if (compatibilityCtor is not null)
{
classDeclaration = classDeclaration.AddMembers(compatibilityCtor);
}

if (ctor is not null)
{
classDeclaration = classDeclaration.AddMembers(ctor);
}
Expand Down Expand Up @@ -568,6 +591,7 @@ private static MemberDeclarationSyntax GenerateDisposeMethod(
INamedTypeSymbol baseClassType)
{
var body = new List<StatementSyntax>();
PoolFieldDescription? poolField = null;
foreach (var field in fields)
{
if (field is CancellationTokenSourceFieldDescription ctsField)
Expand All @@ -582,6 +606,11 @@ private static MemberDeclarationSyntax GenerateDisposeMethod(
MemberBindingExpression(IdentifierName("Dispose"))))));
}

if (field is PoolFieldDescription candidate)
{
poolField = candidate;
}

if (field.IsInstanceField)
{
body.Add(
Expand All @@ -601,6 +630,17 @@ private static MemberDeclarationSyntax GenerateDisposeMethod(
body.Add(ExpressionStatement(InvocationExpression(BaseExpression().Member("Dispose")).WithArgumentList(ArgumentList())));
}

if (poolField is not null)
{
body.Add(
ExpressionStatement(
ConditionalAccessExpression(
IdentifierName(poolField.FieldName),
InvocationExpression(
MemberBindingExpression(IdentifierName("Return")),
ArgumentList(SingletonSeparatedList(Argument(ThisExpression())))))));
}

return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), "Dispose")
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
.WithBody(Block(body));
Expand Down Expand Up @@ -678,9 +718,24 @@ public static string GetSimpleClassName(InvokableMethodDescription method)
return $"Invokable_{method.ContainingInterface.Name}_{proxyKey}_{method.GeneratedMethodId}{typeArgs}";
}

private static TypeSyntax CreateInvokableTypeSyntax(string generatedClassName, InvokableMethodDescription method)
{
if (method.AllTypeParameters.Count == 0)
{
return IdentifierName(generatedClassName);
}

var typeArguments = method.AllTypeParameters.Select(parameter =>
(TypeSyntax)IdentifierName(method.TypeParameterSubstitutions[parameter.Parameter]));
return GenericName(
Identifier(generatedClassName),
TypeArgumentList(SeparatedList(typeArguments)));
}

private MemberDeclarationSyntax[] GetFieldDeclarations(
InvokableMethodDescription method,
List<InvokerFieldDescription> fieldDescriptions)
List<InvokerFieldDescription> fieldDescriptions,
TypeSyntax invokableTypeSyntax)
{
return [.. fieldDescriptions.Select(GetFieldDeclaration)];

Expand Down Expand Up @@ -708,6 +763,14 @@ MemberDeclarationSyntax GetFieldDeclaration(InvokerFieldDescription description)
]))))))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.ReadOnlyKeyword));
}
else if (description is PoolFieldDescription)
{
field = FieldDeclaration(
VariableDeclaration(
LibraryTypes.InvokablePool_1.ToTypeSyntax(invokableTypeSyntax),
SingletonSeparatedList(VariableDeclarator(description.FieldName))))
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ReadOnlyKeyword));
}
else
{
field = FieldDeclaration(
Expand Down Expand Up @@ -738,14 +801,30 @@ private static ExpressionSyntax GetTypesArray(InvokableMethodDescription method,
private (ConstructorDeclarationSyntax? Constructor, List<TypeSyntax> ConstructorArguments) GenerateConstructor(
string simpleClassName,
InvokableMethodDescription method,
INamedTypeSymbol baseClassType)
INamedTypeSymbol baseClassType,
List<InvokerFieldDescription> fieldDescriptions,
TypeSyntax invokableTypeSyntax)
{
var parameters = new List<ParameterSyntax>();

var body = new List<StatementSyntax>();

List<TypeSyntax> constructorArgumentTypes = new();
List<ArgumentSyntax> baseConstructorArguments = new();

if (fieldDescriptions.OfType<PoolFieldDescription>().FirstOrDefault() is { } poolField)
{
var poolType = LibraryTypes.InvokablePool_1.ToTypeSyntax(invokableTypeSyntax);
constructorArgumentTypes.Add(poolType);
parameters.Add(Parameter(Identifier("pool")).WithType(poolType));
body.Add(
ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(poolField.FieldName),
IdentifierName("pool"))));
}

foreach (var constructor in baseClassType.GetAllMembers<IMethodSymbol>())
{
if (constructor.MethodKind != MethodKind.Constructor || constructor.DeclaredAccessibility == Accessibility.Private || constructor.IsImplicitlyDeclared)
Expand Down Expand Up @@ -791,7 +870,9 @@ private static ExpressionSyntax GetTypesArray(InvokableMethodDescription method,
return (constructorDeclaration, constructorArgumentTypes);
}

private List<InvokerFieldDescription> GetFieldDescriptions(InvokableMethodDescription method)
private List<InvokerFieldDescription> GetFieldDescriptions(
InvokableMethodDescription method,
INamedTypeSymbol baseClassType)
{
var fields = new List<InvokerFieldDescription>();
uint fieldId = 0;
Expand All @@ -811,7 +892,27 @@ private List<InvokerFieldDescription> GetFieldDescriptions(InvokableMethodDescri
fields.Add(new CancellationTokenSourceFieldDescription(LibraryTypes));
}

var requiresDependencyInjection = baseClassType.GetAllMembers<IMethodSymbol>()
.Any(constructor =>
constructor.MethodKind == MethodKind.Constructor
&& constructor.HasAttribute(LibraryTypes.GeneratedActivatorConstructorAttribute));
if (method.MethodTypeParameters.Count == 0
&& method.Method.Parameters.Length >= 2
&& method.CustomInitializerMethods.Count == 0
&& !requiresDependencyInjection
&& IsPoolableBaseType(baseClassType))
{
fields.Add(new PoolFieldDescription(LibraryTypes));
}

return fields;

static bool IsPoolableBaseType(INamedTypeSymbol type)
=> type.ContainingNamespace.ToDisplayString() == "Orleans.Runtime"
&& type.MetadataName is "Request"
or "Request`1"
or "TaskRequest"
or "TaskRequest`1";
}

internal abstract class InvokerFieldDescription(ITypeSymbol fieldType, string fieldName)
Expand Down Expand Up @@ -910,4 +1011,10 @@ internal sealed class MethodInfoFieldDescription(ITypeSymbol fieldType, string f
public override bool IsSerializable => false;
public override bool IsInstanceField => false;
}

internal sealed class PoolFieldDescription(LibraryTypes libraryTypes) : InvokerFieldDescription(libraryTypes.InvokablePool_1, "_pool")
{
public override bool IsSerializable => false;
public override bool IsInstanceField => false;
}
}
2 changes: 2 additions & 0 deletions src/Orleans.CodeGenerator/LibraryTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ private LibraryTypes(Compilation compilation, CodeGeneratorOptions options)
GenerateSerializerAttribute = Type("Orleans.GenerateSerializerAttribute");
SerializationCallbacksAttribute = Type("Orleans.SerializationCallbacksAttribute");
IActivator_1 = Type("Orleans.Serialization.Activators.IActivator`1");
InvokablePool_1 = Type("Orleans.Serialization.Invocation.InvokablePool`1");
IBufferWriter = Type("System.Buffers.IBufferWriter`1");
IdAttributeType = Type(CodeGeneratorOptions.IdAttribute);
ConstructorAttributeTypes = [.. CodeGeneratorOptions.ConstructorAttributes.Select(Type)];
Expand Down Expand Up @@ -213,6 +214,7 @@ INamedTypeSymbol Type(string metadataName)
public INamedTypeSymbol GenerateMethodSerializersAttribute { get; private set; }
public INamedTypeSymbol GenerateSerializerAttribute { get; private set; }
public INamedTypeSymbol IActivator_1 { get; private set; }
public INamedTypeSymbol InvokablePool_1 { get; private set; }
public INamedTypeSymbol IBufferWriter { get; private set; }
public INamedTypeSymbol IInvokable { get; private set; }
public INamedTypeSymbol ITargetHolder { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public GeneratedInvokableDescription(
List<INamedTypeSymbol> serializationHooks,
INamedTypeSymbol baseType,
List<TypeSyntax> constructorArguments,
bool usesInvokablePool,
List<CompoundTypeAliasComponent[]> compoundTypeAliases,
string? returnValueInitializerMethod,
ClassDeclarationSyntax classDeclarationSyntax)
Expand All @@ -39,6 +40,7 @@ public GeneratedInvokableDescription(
Accessibility = accessibility;
SerializationHooks = serializationHooks;
ActivatorConstructorParameters = constructorArguments;
UsesInvokablePool = usesInvokablePool;
CompoundTypeAliases = compoundTypeAliases;
ReturnValueInitializerMethod = returnValueInitializerMethod;
ClassDeclarationSyntax = classDeclarationSyntax;
Expand Down Expand Up @@ -72,6 +74,7 @@ public GeneratedInvokableDescription(
public bool IsImmutable => false;
public bool IsExceptionType => false;
public List<TypeSyntax> ActivatorConstructorParameters { get; }
public bool UsesInvokablePool { get; }
public bool HasActivatorConstructor => UseActivator;
public List<CompoundTypeAliasComponent[]> CompoundTypeAliases { get; }
public ClassDeclarationSyntax ClassDeclarationSyntax { get; }
Expand Down
7 changes: 4 additions & 3 deletions src/Orleans.CodeGenerator/Model/ProxyMethodDescription.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Orleans.CodeGenerator.SyntaxGeneration;
using Microsoft.CodeAnalysis;
using System.Diagnostics;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Orleans.CodeGenerator.SyntaxGeneration;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Orleans.CodeGenerator;
Expand Down Expand Up @@ -148,6 +148,7 @@ public ConstructedGeneratedInvokableDescription(GeneratedInvokableDescription in
public bool IsImmutable => _invokableDescription.IsImmutable;
public bool IsExceptionType => _invokableDescription.IsExceptionType;
public List<TypeSyntax> ActivatorConstructorParameters => _invokableDescription.ActivatorConstructorParameters;
public bool UsesInvokablePool => _invokableDescription.UsesInvokablePool;
public bool HasActivatorConstructor => UseActivator;
public string? ReturnValueInitializerMethod => _invokableDescription.ReturnValueInitializerMethod;

Expand Down
Loading