Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
63fd7d5
Add incremental source generator work
ReubenBond Apr 23, 2026
6488e13
Simplify incremental generator model equality
ReubenBond Apr 24, 2026
6458339
Improve source generator build performance
ReubenBond Apr 24, 2026
54d8bea
Optimize incremental source generator performance
ReubenBond Apr 28, 2026
9c51a13
Use metadata identities for resolver
ReubenBond Apr 28, 2026
f1efb2a
Fix transaction request serialization
ReubenBond Apr 28, 2026
a66a632
Remove legacy code generator facade
ReubenBond Apr 28, 2026
aaac1df
Emit generated metadata last
ReubenBond Apr 28, 2026
abe8c3a
Minimize metadata ordering snapshot churn
ReubenBond Apr 28, 2026
3388fa7
Remove source generator snapshot churn
ReubenBond Apr 28, 2026
7c5c98c
Reduce code generator model churn
ReubenBond Apr 28, 2026
3d742c7
Rename compound alias emission helper
ReubenBond Apr 28, 2026
c86f9fa
Restore generated invokable activator metadata
ReubenBond Apr 28, 2026
2901205
Close incremental generator parity gaps
ReubenBond Apr 28, 2026
3e5fc6c
Fix net10 referenced diagnostics
ReubenBond Apr 28, 2026
639aa79
Address code generator review feedback
ReubenBond Apr 28, 2026
57ad67f
Handle record primary constructor field ids
ReubenBond Apr 28, 2026
1c8bd07
Add incremental source generator regression tests
ReubenBond Apr 29, 2026
844897d
Preserve record field id wire format
ReubenBond Apr 29, 2026
3577f57
cleanup
ReubenBond Apr 29, 2026
304af04
Use structural equality wrapper for CodeGenerator models
ReubenBond Apr 29, 2026
2838cb7
Split CodeGenerator helpers into named classes
ReubenBond Apr 29, 2026
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
2 changes: 2 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ProjectReference
Include="$(SourceRoot)src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
PrivateAssets="None"
Condition=" '$(OrleansBuildTimeCodeGen)' == 'true' "/>
<ProjectReference
Expand All @@ -22,6 +23,7 @@
UndefineProperties="TargetFramework"
SkipGetTargetFrameworkProperties="true"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
PrivateAssets="None"
Condition=" '$(OrleansBuildTimeCodeGen)' == 'true' "/>
</ItemGroup>
Expand Down
197 changes: 103 additions & 94 deletions src/Orleans.CodeGenerator/ActivatorGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,128 +2,137 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using System.Collections.Generic;

namespace Orleans.CodeGenerator
namespace Orleans.CodeGenerator;

internal class ActivatorGenerator(IGeneratorServices generatorServices)
{
internal class ActivatorGenerator
private readonly IGeneratorServices _generatorServices = generatorServices;

private struct ConstructorArgument
{
public TypeSyntax Type { get; set; }
public string FieldName { get; set; }
public string ParameterName { get; set; }
}

public ClassDeclarationSyntax GenerateActivator(ISerializableTypeDescription type)
{
private readonly CodeGenerator _codeGenerator;
var simpleClassName = GetSimpleClassName(type);

var baseInterface = _generatorServices.LibraryTypes.IActivator_1.ToTypeSyntax(type.TypeSyntax);

private struct ConstructorArgument
var orderedFields = new List<ConstructorArgument>();
var index = 0;
if (type.ActivatorConstructorParameters is { Count: > 0 } parameters)
{
public TypeSyntax Type { get; set; }
public string FieldName { get; set; }
public string ParameterName { get; set; }
foreach (var arg in parameters)
{
orderedFields.Add(new ConstructorArgument { Type = arg, FieldName = $"_arg{index}", ParameterName = $"arg{index}" });
index++;
}
}

public ActivatorGenerator(CodeGenerator codeGenerator)
var members = new List<MemberDeclarationSyntax>();
foreach (var field in orderedFields)
{
_codeGenerator = codeGenerator;
members.Add(
FieldDeclaration(VariableDeclaration(field.Type, SingletonSeparatedList(VariableDeclarator(field.FieldName))))
.AddModifiers(
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.ReadOnlyKeyword)));
}

public ClassDeclarationSyntax GenerateActivator(ISerializableTypeDescription type)
{
var simpleClassName = GetSimpleClassName(type);
if (orderedFields.Count > 0)
members.Add(GenerateConstructor(simpleClassName, orderedFields));

var baseInterface = _codeGenerator.LibraryTypes.IActivator_1.ToTypeSyntax(type.TypeSyntax);
members.Add(GenerateCreateMethod(type, orderedFields));

var orderedFields = new List<ConstructorArgument>();
var index = 0;
if (type.ActivatorConstructorParameters is { Count: > 0 } parameters)
{
foreach (var arg in parameters)
{
orderedFields.Add(new ConstructorArgument { Type = arg, FieldName = $"_arg{index}", ParameterName = $"arg{index}" });
index++;
}
}
var classDeclaration = ClassDeclaration(simpleClassName)
.AddBaseListTypes(SimpleBaseType(baseInterface))
.AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
.AddAttributeLists(GeneratedCodeUtilities.GetGeneratedCodeAttributes())
.AddMembers([.. members]);

var members = new List<MemberDeclarationSyntax>();
foreach (var field in orderedFields)
{
members.Add(
FieldDeclaration(VariableDeclaration(field.Type, SingletonSeparatedList(VariableDeclarator(field.FieldName))))
.AddModifiers(
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.ReadOnlyKeyword)));
}

if (orderedFields.Count > 0)
members.Add(GenerateConstructor(simpleClassName, orderedFields));
if (type.IsGenericType)
{
classDeclaration = SyntaxFactoryUtility.AddGenericTypeParameters(classDeclaration, type.TypeParameters);
}

members.Add(GenerateCreateMethod(type, orderedFields));
return classDeclaration;
}

var classDeclaration = ClassDeclaration(simpleClassName)
.AddBaseListTypes(SimpleBaseType(baseInterface))
.AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
.AddAttributeLists(CodeGenerator.GetGeneratedCodeAttributes())
.AddMembers(members.ToArray());
public static string GetSimpleClassName(ISerializableTypeDescription serializableType) => GetSimpleClassName(serializableType.Name);

if (type.IsGenericType)
{
classDeclaration = SyntaxFactoryUtility.AddGenericTypeParameters(classDeclaration, type.TypeParameters);
}
public static string GetSimpleClassName(string name) => $"Activator_{name}";

return classDeclaration;
}

public static string GetSimpleClassName(ISerializableTypeDescription serializableType) => $"Activator_{serializableType.Name}";
/// <summary>
/// Determines whether an activator should be generated for the specified type.
/// </summary>
internal static bool ShouldGenerateActivator(ISerializableTypeDescription type)
{
return !type.IsAbstractType
&& !type.IsEnumType
&& (!type.IsValueType
&& type.IsEmptyConstructable
&& !type.UseActivator
&& type is not GeneratedInvokableDescription
|| type.HasActivatorConstructor);
}

private ConstructorDeclarationSyntax GenerateConstructor(
string simpleClassName,
List<ConstructorArgument> orderedFields)
private static ConstructorDeclarationSyntax GenerateConstructor(
string simpleClassName,
List<ConstructorArgument> orderedFields)
{
var parameters = new List<ParameterSyntax>();
var body = new List<StatementSyntax>();
foreach (var field in orderedFields)
{
var parameters = new List<ParameterSyntax>();
var body = new List<StatementSyntax>();
foreach (var field in orderedFields)
{
parameters.Add(Parameter(field.ParameterName.ToIdentifier()).WithType(field.Type));
parameters.Add(Parameter(field.ParameterName.ToIdentifier()).WithType(field.Type));

body.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
field.FieldName.ToIdentifierName(),
Unwrapped(field.ParameterName.ToIdentifierName()))));
}
body.Add(ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
field.FieldName.ToIdentifierName(),
Unwrapped(field.ParameterName.ToIdentifierName()))));
}

var constructorDeclaration = ConstructorDeclaration(simpleClassName)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters(parameters.ToArray())
.AddBodyStatements(body.ToArray());
var constructorDeclaration = ConstructorDeclaration(simpleClassName)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddParameterListParameters([.. parameters])
.AddBodyStatements([.. body]);

return constructorDeclaration;
return constructorDeclaration;

static ExpressionSyntax Unwrapped(ExpressionSyntax expr)
{
return InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("OrleansGeneratedCodeHelper"), IdentifierName("UnwrapService")),
ArgumentList(SeparatedList(new[] { Argument(ThisExpression()), Argument(expr) })));
}
static ExpressionSyntax Unwrapped(ExpressionSyntax expr)
{
return InvocationExpression(
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("OrleansGeneratedCodeHelper"), IdentifierName("UnwrapService")),
ArgumentList(SeparatedList([Argument(ThisExpression()), Argument(expr)])));
}
}

private MemberDeclarationSyntax GenerateCreateMethod(ISerializableTypeDescription type, List<ConstructorArgument> orderedFields)
private static MemberDeclarationSyntax GenerateCreateMethod(ISerializableTypeDescription type, List<ConstructorArgument> orderedFields)
{
ExpressionSyntax createObject;
if (type.ActivatorConstructorParameters is { Count: > 0 })
{
ExpressionSyntax createObject;
if (type.ActivatorConstructorParameters is { Count: > 0 })
{
var argList = new List<ArgumentSyntax>();
foreach (var field in orderedFields)
{
argList.Add(Argument(field.FieldName.ToIdentifierName()));
}

createObject = ObjectCreationExpression(type.TypeSyntax).WithArgumentList(ArgumentList(SeparatedList(argList)));
}
else
var argList = new List<ArgumentSyntax>();
foreach (var field in orderedFields)
{
createObject = type.GetObjectCreationExpression();
argList.Add(Argument(field.FieldName.ToIdentifierName()));
}

return MethodDeclaration(type.TypeSyntax, "Create")
.WithExpressionBody(ArrowExpressionClause(createObject))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
.AddModifiers(Token(SyntaxKind.PublicKeyword));
createObject = ObjectCreationExpression(type.TypeSyntax).WithArgumentList(ArgumentList(SeparatedList(argList)));
}
else
{
createObject = type.GetObjectCreationExpression();
}

return MethodDeclaration(type.TypeSyntax, "Create")
.WithExpressionBody(ArrowExpressionClause(createObject))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
.AddModifiers(Token(SyntaxKind.PublicKeyword));
}
}
}
36 changes: 17 additions & 19 deletions src/Orleans.CodeGenerator/ApplicationPartAttributeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
using System.Collections.Generic;
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
namespace Orleans.CodeGenerator;

internal static class ApplicationPartAttributeGenerator
{
internal static class ApplicationPartAttributeGenerator
public static List<AttributeListSyntax> GenerateSyntax(NameSyntax applicationPartAttribute, IEnumerable<string> applicationParts)
{
public static List<AttributeListSyntax> GenerateSyntax(LibraryTypes wellKnownTypes, MetadataModel model)
{
var attributes = new List<AttributeListSyntax>();
var attributes = new List<AttributeListSyntax>();

foreach (var assemblyName in model.ApplicationParts)
{
// Generate an assembly-level attribute with an instance of that class.
var attribute = AttributeList(
AttributeTargetSpecifier(Token(SyntaxKind.AssemblyKeyword)),
SingletonSeparatedList(
Attribute(wellKnownTypes.ApplicationPartAttribute.ToNameSyntax())
.AddArgumentListArguments(AttributeArgument(assemblyName.GetLiteralExpression()))));
attributes.Add(attribute);
}

return attributes;
foreach (var assemblyName in applicationParts)
{
// Generate an assembly-level attribute with an instance of that class.
var attribute = AttributeList(
AttributeTargetSpecifier(Token(SyntaxKind.AssemblyKeyword)),
SingletonSeparatedList(
Attribute(applicationPartAttribute)
.AddArgumentListArguments(AttributeArgument(assemblyName.GetLiteralExpression()))));
attributes.Add(attribute);
}

return attributes;
}
}
Loading
Loading