IpcServiceGenerator.cs
1 using Microsoft.CodeAnalysis; 2 using Microsoft.CodeAnalysis.CSharp; 3 using Microsoft.CodeAnalysis.CSharp.Syntax; 4 using System.Linq; 5 6 namespace Ryujinx.HLE.Generators 7 { 8 [Generator] 9 public class IpcServiceGenerator : ISourceGenerator 10 { 11 public void Execute(GeneratorExecutionContext context) 12 { 13 var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver; 14 CodeGenerator generator = new CodeGenerator(); 15 16 generator.AppendLine("using System;"); 17 generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm"); 18 generator.EnterScope($"partial class IUserInterface"); 19 20 generator.EnterScope($"public IpcService? GetServiceInstance(Type type, ServiceCtx context, object? parameter = null)"); 21 foreach (var className in syntaxReceiver.Types) 22 { 23 if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service")))) 24 continue; 25 var name = GetFullName(className, context).Replace("global::", ""); 26 if (!name.StartsWith("Ryujinx.HLE.HOS.Services")) 27 continue; 28 var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax); 29 30 if (!constructors.Any(x => x.ParameterList.Parameters.Count >= 1)) 31 continue; 32 33 if (constructors.Where(x => x.ParameterList.Parameters.Count >= 1).FirstOrDefault().ParameterList.Parameters[0].Type.ToString() == "ServiceCtx") 34 { 35 generator.EnterScope($"if (type == typeof({GetFullName(className, context)}))"); 36 if (constructors.Any(x => x.ParameterList.Parameters.Count == 2)) 37 { 38 var type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type; 39 var model = context.Compilation.GetSemanticModel(type.SyntaxTree); 40 var typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol; 41 var fullName = typeSymbol.ToString(); 42 generator.EnterScope("if (parameter != null)"); 43 generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);"); 44 generator.LeaveScope(); 45 } 46 47 if (constructors.Any(x => x.ParameterList.Parameters.Count == 1)) 48 { 49 generator.AppendLine($"return new {GetFullName(className, context)}(context);"); 50 } 51 52 generator.LeaveScope(); 53 } 54 } 55 56 generator.AppendLine("return null;"); 57 generator.LeaveScope(); 58 59 generator.LeaveScope(); 60 generator.LeaveScope(); 61 context.AddSource($"IUserInterface.g.cs", generator.ToString()); 62 } 63 64 private string GetFullName(ClassDeclarationSyntax syntaxNode, GeneratorExecutionContext context) 65 { 66 var typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode); 67 68 return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 69 } 70 71 public void Initialize(GeneratorInitializationContext context) 72 { 73 context.RegisterForSyntaxNotifications(() => new ServiceSyntaxReceiver()); 74 } 75 } 76 }