From 1a55a553eabb29a26fc81f43ef2689305bc6966d Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 19 Nov 2025 00:33:39 -0600 Subject: [PATCH] Convert HLE.Generators to IIncrementalSourceGenerator --- Directory.Packages.props | 3 +- .../IpcServiceGenerator.cs | 118 +++++++++--------- .../Ryujinx.HLE.Generators.csproj | 6 +- .../ServiceSyntaxReceiver.cs | 24 ---- 4 files changed, 65 insertions(+), 86 deletions(-) delete mode 100644 src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index fd61602a8..4a74eba02 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,6 +38,7 @@ + @@ -59,4 +60,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs index cdd062826..a5c8e9c5f 100644 --- a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs +++ b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs @@ -6,73 +6,73 @@ using System.Linq; namespace Ryujinx.HLE.Generators { [Generator] - public class IpcServiceGenerator : ISourceGenerator + public sealed class IpcServiceGenerator : IIncrementalGenerator { - public void Execute(GeneratorExecutionContext context) + private sealed record ServiceData { - ServiceSyntaxReceiver syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver; - CodeGenerator generator = new(); - - generator.AppendLine("#nullable enable"); - generator.AppendLine("using System;"); - generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm"); - generator.EnterScope($"partial class IUserInterface"); - - generator.EnterScope($"public IpcService? GetServiceInstance(Type type, ServiceCtx context, object? parameter = null)"); - foreach (ClassDeclarationSyntax className in syntaxReceiver.Types) - { - if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service")))) - continue; - string name = GetFullName(className, context).Replace("global::", string.Empty); - if (!name.StartsWith("Ryujinx.HLE.HOS.Services")) - continue; - ConstructorDeclarationSyntax[] constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax).ToArray(); - - if (!constructors.Any(x => x.ParameterList.Parameters.Count >= 1)) - continue; - - if (constructors.Where(x => x.ParameterList.Parameters.Count >= 1).FirstOrDefault().ParameterList.Parameters[0].Type.ToString() == "ServiceCtx") + public required string FullName { get; init; } + public required bool HasOneParamCtor { get; init; } + public required bool HasTwoParamCtor { get; init; } + public required string SecondParamTypeFullName { get; init; } + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var pipeline = context.SyntaxProvider.ForAttributeWithMetadataName("Ryujinx.HLE.HOS.Services.ServiceAttribute", + predicate: (node, _) => node is ClassDeclarationSyntax decl && !decl.Modifiers.Any(SyntaxKind.AbstractKeyword) && !decl.Modifiers.Any(SyntaxKind.PrivateKeyword), + transform: (ctx, _) => { - generator.EnterScope($"if (type == typeof({GetFullName(className, context)}))"); - if (constructors.Any(x => x.ParameterList.Parameters.Count == 2)) + var target = (INamedTypeSymbol)ctx.TargetSymbol; + var twoParamCtor = target.Constructors.FirstOrDefault(ctor => ctor.Parameters.Length == 2); + return new ServiceData { - TypeSyntax type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type; - SemanticModel model = context.Compilation.GetSemanticModel(type.SyntaxTree); - INamedTypeSymbol typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol; - string fullName = typeSymbol.ToString(); - generator.EnterScope("if (parameter != null)"); - generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);"); + FullName = target.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + HasOneParamCtor = target.Constructors.Any(ctor => ctor.Parameters.Length == 1), + HasTwoParamCtor = twoParamCtor != null, + SecondParamTypeFullName = twoParamCtor?.Parameters[1].Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + }; + } + ) + .Where(data => data.HasOneParamCtor || data.HasTwoParamCtor); + + context.RegisterSourceOutput(pipeline.Collect(), + (ctx, data) => + { + var generator = new CodeGenerator(); + + generator.AppendLine("#nullable enable"); + generator.AppendLine("using System;"); + generator.EnterScope("namespace Ryujinx.HLE.HOS.Services.Sm"); + generator.EnterScope("partial class IUserInterface"); + + generator.EnterScope("public IpcService? GetServiceInstance(Type type, ServiceCtx context, object? parameter = null)"); + + foreach (var service in data) + { + generator.EnterScope($"if (type == typeof({service.FullName}))"); + if (service.HasTwoParamCtor) + { + generator.EnterScope("if (parameter != null)"); + generator.AppendLine($"return new {service.FullName}(context, ({service.SecondParamTypeFullName})parameter);"); + generator.LeaveScope(); + } + if (service.HasOneParamCtor) + { + generator.AppendLine($"return new {service.FullName}(context);"); + } generator.LeaveScope(); } - - if (constructors.Any(x => x.ParameterList.Parameters.Count == 1)) - { - generator.AppendLine($"return new {GetFullName(className, context)}(context);"); - } + + generator.AppendLine("return null;"); + generator.LeaveScope(); generator.LeaveScope(); - } - } - - generator.AppendLine("return null;"); - generator.LeaveScope(); - - generator.LeaveScope(); - generator.LeaveScope(); - generator.AppendLine("#nullable disable"); - context.AddSource($"IUserInterface.g.cs", generator.ToString()); - } - - private string GetFullName(ClassDeclarationSyntax syntaxNode, GeneratorExecutionContext context) - { - INamedTypeSymbol typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode); - - return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - } - - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(() => new ServiceSyntaxReceiver()); + generator.LeaveScope(); + + generator.AppendLine("#nullable disable"); + + ctx.AddSource("IUserInterface.g.cs", generator.ToString()); + }); } } } diff --git a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj index 4791a3b27..5de37c865 100644 --- a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj +++ b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj @@ -3,8 +3,6 @@ netstandard2.0 true - true - Generated true $(DefaultItemExcludes);._* @@ -15,6 +13,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs b/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs deleted file mode 100644 index 7513f5f45..000000000 --- a/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Generic; - -namespace Ryujinx.HLE.Generators -{ - internal class ServiceSyntaxReceiver : ISyntaxReceiver - { - public HashSet Types = []; - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is ClassDeclarationSyntax classDeclaration) - { - if (classDeclaration.BaseList == null) - { - return; - } - - Types.Add(classDeclaration); - } - } - } -}