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;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Ryujinx.HLE.Generators namespace Ryujinx.HLE.Generators
{ {
[Generator] [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 string FullName { get; init; }
public required bool HasOneParamCtor { get; init; } public required IReadOnlyList<(string ServiceName, string ParameterValue)> Instances { get; init; }
public required bool HasTwoParamCtor { get; init; }
public required string SecondParamTypeFullName { 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) public void Initialize(IncrementalGeneratorInitializationContext context)
@ -23,17 +33,18 @@ namespace Ryujinx.HLE.Generators
transform: (ctx, _) => transform: (ctx, _) =>
{ {
var target = (INamedTypeSymbol)ctx.TargetSymbol; 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 return new ServiceData
{ {
FullName = target.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), FullName = target.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
HasOneParamCtor = target.Constructors.Any(ctor => ctor.Parameters.Length == 1), Instances = instances.ToList(),
HasTwoParamCtor = twoParamCtor != null,
SecondParamTypeFullName = twoParamCtor?.Parameters[1].Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
}; };
} }
) );
.Where(data => data.HasOneParamCtor || data.HasTwoParamCtor);
context.RegisterSourceOutput(pipeline.Collect(), context.RegisterSourceOutput(pipeline.Collect(),
(ctx, data) => (ctx, data) =>
@ -45,25 +56,29 @@ namespace Ryujinx.HLE.Generators
generator.EnterScope("namespace Ryujinx.HLE.HOS.Services.Sm"); generator.EnterScope("namespace Ryujinx.HLE.HOS.Services.Sm");
generator.EnterScope("partial class IUserInterface"); 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}))"); foreach (var instance in serviceImpl.Instances)
if (service.HasTwoParamCtor)
{ {
generator.EnterScope("if (parameter != null)"); if (instance.ParameterValue == null)
generator.AppendLine($"return new {service.FullName}(context, ({service.SecondParamTypeFullName})parameter);"); {
generator.LeaveScope(); 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();
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 [Service("acc:aa", AccountServiceFlag.BaasAccessTokenAccessor)] // Max Sessions: 4
class IBaasAccessTokenAccessor : IpcService 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 namespace Ryujinx.HLE.HOS.Services
{ {
abstract class IpcService abstract partial class IpcService
{ {
public IReadOnlyDictionary<int, MethodInfo> CmifCommands { get; } public IReadOnlyDictionary<int, MethodInfo> CmifCommands { get; }
public IReadOnlyDictionary<int, MethodInfo> TipcCommands { 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.HLE.HOS.Kernel.Ipc;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
namespace Ryujinx.HLE.HOS.Services.Sm namespace Ryujinx.HLE.HOS.Services.Sm
{ {
partial class IUserInterface : IpcService partial class IUserInterface : IpcService
{ {
private static readonly Dictionary<string, Type> _services;
private readonly SmRegistry _registry; private readonly SmRegistry _registry;
private readonly ServerBase _commonServer; private readonly ServerBase _commonServer;
@ -27,14 +22,6 @@ namespace Ryujinx.HLE.HOS.Services.Sm
_registry = registry; _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)] [CommandCmif(0)]
[CommandTipc(0)] // 12.0.0+ [CommandTipc(0)] // 12.0.0+
// Initialize(pid, u64 reserved) // Initialize(pid, u64 reserved)
@ -91,12 +78,8 @@ namespace Ryujinx.HLE.HOS.Services.Sm
} }
else 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.TrySetServer(_commonServer);
service.Server.AddSessionObj(session.ServerSession, service); service.Server.AddSessionObj(session.ServerSession, service);
} }