Replace GetServiceInstance with fully source-generated version

This commit is contained in:
Aaron Robinson
2025-11-19 03:51:07 -06:00
parent 1a55a553ea
commit 19de0d0db6
4 changed files with 43 additions and 44 deletions

View File

@@ -1,19 +1,29 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.HLE.Generators
{
[Generator]
public sealed class IpcServiceGenerator : IIncrementalGenerator
public sealed class UserServiceGenerator : IIncrementalGenerator
{
private sealed record ServiceData
private sealed class ServiceData : IEquatable<ServiceData>
{
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 required IReadOnlyList<(string ServiceName, string ParameterValue)> Instances { get; init; }
public override bool Equals(object obj)
=> obj is ServiceData data && Equals(data);
public bool Equals(ServiceData other)
{
return this.FullName == other.FullName && this.Instances.SequenceEqual(other.Instances);
}
public override int GetHashCode() => FullName.GetHashCode();
}
public void Initialize(IncrementalGeneratorInitializationContext context)
@@ -23,17 +33,18 @@ namespace Ryujinx.HLE.Generators
transform: (ctx, _) =>
{
var target = (INamedTypeSymbol)ctx.TargetSymbol;
var twoParamCtor = target.Constructors.FirstOrDefault(ctor => ctor.Parameters.Length == 2);
var instances = ctx.Attributes.Select(attr =>
{
string param = attr.ConstructorArguments is [_, { IsNull: false } arg] ? arg.ToCSharpString() : null;
return ((string)attr.ConstructorArguments[0].Value, param);
});
return new ServiceData
{
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),
Instances = instances.ToList(),
};
}
)
.Where(data => data.HasOneParamCtor || data.HasTwoParamCtor);
);
context.RegisterSourceOutput(pipeline.Collect(),
(ctx, data) =>
@@ -45,25 +56,29 @@ namespace Ryujinx.HLE.Generators
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)");
generator.EnterScope("public IpcService? GetServiceInstance(string name, ServiceCtx context)");
generator.EnterScope("return name switch");
foreach (var service in data)
foreach (var serviceImpl in data)
{
generator.EnterScope($"if (type == typeof({service.FullName}))");
if (service.HasTwoParamCtor)
foreach (var instance in serviceImpl.Instances)
{
generator.EnterScope("if (parameter != null)");
generator.AppendLine($"return new {service.FullName}(context, ({service.SecondParamTypeFullName})parameter);");
generator.LeaveScope();
if (instance.ParameterValue == null)
{
generator.AppendLine($"\"{instance.ServiceName}\" => new {serviceImpl.FullName}(context),");
}
else
{
generator.AppendLine($"\"{instance.ServiceName}\" => new {serviceImpl.FullName}(context, {instance.ParameterValue}),");
}
}
if (service.HasOneParamCtor)
{
generator.AppendLine($"return new {service.FullName}(context);");
}
generator.LeaveScope();
}
generator.AppendLine("return null;");
generator.AppendLine("_ => null,");
generator.LeaveScope(";");
generator.LeaveScope();
generator.LeaveScope();

View File

@@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
[Service("acc:aa", AccountServiceFlag.BaasAccessTokenAccessor)] // Max Sessions: 4
class IBaasAccessTokenAccessor : IpcService
{
public IBaasAccessTokenAccessor(ServiceCtx context) { }
public IBaasAccessTokenAccessor(ServiceCtx context, AccountServiceFlag serviceFlag) { }
}
}

View File

@@ -11,8 +11,9 @@ using System.Reflection;
namespace Ryujinx.HLE.HOS.Services
{
abstract class IpcService
abstract partial class IpcService
{
public IReadOnlyDictionary<int, MethodInfo> CmifCommands { get; }
public IReadOnlyDictionary<int, MethodInfo> TipcCommands { get; }

View File

@@ -4,18 +4,13 @@ using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.Horizon.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Sm
{
partial class IUserInterface : IpcService
{
private static readonly Dictionary<string, Type> _services;
private readonly SmRegistry _registry;
private readonly ServerBase _commonServer;
@@ -27,14 +22,6 @@ namespace Ryujinx.HLE.HOS.Services.Sm
_registry = registry;
}
static IUserInterface()
{
_services = typeof(IUserInterface).Assembly.GetTypes()
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
.Select(service => (((ServiceAttribute)service).Name, type)))
.ToDictionary(service => service.Name, service => service.type);
}
[CommandCmif(0)]
[CommandTipc(0)] // 12.0.0+
// Initialize(pid, u64 reserved)
@@ -91,12 +78,8 @@ namespace Ryujinx.HLE.HOS.Services.Sm
}
else
{
if (_services.TryGetValue(name, out Type type))
if (GetServiceInstance(name, context) is { } service)
{
ServiceAttribute serviceAttribute = type.GetCustomAttributes<ServiceAttribute>().First(service => service.Name == name);
IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);
service.TrySetServer(_commonServer);
service.Server.AddSessionObj(session.ServerSession, service);
}