Refactor `ServiceInjector`
This commit is contained in:
parent
c5d6a66e01
commit
70bd081a93
|
@ -2,6 +2,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
|
@ -62,7 +63,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
tuple =>
|
||||
{
|
||||
var instance = tuple.Ctor.Invoke(tuple.CtorTypes.Select(t => avail[t]).ToArray());
|
||||
ServiceInjector.UpdateServices(serviceProvider, instance);
|
||||
Debug.Assert(ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true));
|
||||
return (IExternalApi) instance;
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
@ -65,7 +66,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|| lib.GetCustomAttribute<LuaLibraryAttribute>(inherit: false)?.Released is not false)
|
||||
{
|
||||
var instance = (LuaLibraryBase)Activator.CreateInstance(lib, this, _apiContainer, (Action<string>)LogToLuaConsole);
|
||||
ServiceInjector.UpdateServices(serviceProvider, instance);
|
||||
Debug.Assert(ServiceInjector.UpdateServices(serviceProvider, instance, mayCache: true));
|
||||
|
||||
// TODO: make EmuHawk libraries have a base class with common properties such as this
|
||||
// and inject them here
|
||||
|
@ -181,7 +182,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
foreach (var lib in Libraries.Values)
|
||||
{
|
||||
lib.APIs = _apiContainer;
|
||||
ServiceInjector.UpdateServices(newServiceProvider, lib);
|
||||
Debug.Assert(ServiceInjector.UpdateServices(newServiceProvider, lib, mayCache: true));
|
||||
lib.Restarted();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
if (!(CreateInstance<T>(toolPath) is T newTool)) return null;
|
||||
|
||||
if (newTool is Form form) form.Owner = _owner;
|
||||
if (!ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool)) return null;
|
||||
if (!ServiceInjector.UpdateServices(_emulator.ServiceProvider, newTool)) return null; //TODO pass `true` for `mayCache` when from EmuHawk assembly
|
||||
SetBaseProperties(newTool);
|
||||
var toolTypeName = typeof(T).FullName!;
|
||||
// auto settings
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using BizHawk.Common.ReflectionExtensions;
|
||||
|
||||
|
@ -10,30 +12,71 @@ namespace BizHawk.Emulation.Common
|
|||
/// </summary>
|
||||
public static class ServiceInjector
|
||||
{
|
||||
private readonly struct ServicePropInfo
|
||||
{
|
||||
public readonly Type PropType;
|
||||
|
||||
public readonly MethodInfo Setter;
|
||||
|
||||
public ServicePropInfo(Type propType, MethodInfo setter)
|
||||
{
|
||||
PropType = propType;
|
||||
Setter = setter;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, (List<ServicePropInfo> Req, List<ServicePropInfo> Opt)> _cache = new();
|
||||
|
||||
private static (List<ServicePropInfo> Req, List<ServicePropInfo> Opt) GetServicePropsFor(
|
||||
this Type @class,
|
||||
bool mayCache)
|
||||
{
|
||||
if (_cache.TryGetValue(@class, out var pair)) return pair;
|
||||
pair = (new(), new());
|
||||
foreach (var pi in @class.GetProperties(ReflectionExtensions.DI_TARGET_PROPS))
|
||||
{
|
||||
//TODO enumerate attrs only once
|
||||
if (pi.GetCustomAttributes(typeof(RequiredServiceAttribute), inherit: false).Length is not 0)
|
||||
{
|
||||
pair.Req.Add(new(pi.PropertyType, pi.GetSetMethod(nonPublic: true)));
|
||||
}
|
||||
else if (pi.GetCustomAttributes(typeof(OptionalServiceAttribute), inherit: false).Length is not 0)
|
||||
{
|
||||
pair.Opt.Add(new(pi.PropertyType, pi.GetSetMethod(nonPublic: true)));
|
||||
}
|
||||
}
|
||||
if (mayCache) _cache[@class] = pair;
|
||||
return pair;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Feeds the target its required services.
|
||||
/// </summary>
|
||||
/// <param name="mayCache">
|
||||
/// <see langword="true"/> if the properties of <paramref name="target"/> may be written to cache,
|
||||
/// i.e. if it's known to come from a first-party assembly and not an ext. tool.<br/>
|
||||
/// Cache will still be read from regardless.
|
||||
/// </param>
|
||||
/// <returns>false if update failed</returns>
|
||||
public static bool UpdateServices(IEmulatorServiceProvider source, object target)
|
||||
/// <remarks>don't think having a genericised overload would be helpful, but TODO pass in type to save <c>target.GetType()</c> call</remarks>
|
||||
public static bool UpdateServices(IEmulatorServiceProvider source, object target, bool mayCache = false)
|
||||
{
|
||||
Type targetType = target.GetType();
|
||||
object?[] tmp = new object?[1];
|
||||
|
||||
foreach (var propInfo in targetType.GetPropertiesWithAttrib(typeof(RequiredServiceAttribute)))
|
||||
var (req, opt) = GetServicePropsFor(targetType, mayCache: mayCache);
|
||||
foreach (var info in req)
|
||||
{
|
||||
tmp[0] = source.GetService(propInfo.PropertyType);
|
||||
tmp[0] = source.GetService(info.PropType);
|
||||
if (tmp[0] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
propInfo.GetSetMethod(true).Invoke(target, tmp);
|
||||
info.Setter.Invoke(target, tmp);
|
||||
}
|
||||
|
||||
foreach (var propInfo in targetType.GetPropertiesWithAttrib(typeof(OptionalServiceAttribute)))
|
||||
foreach (var info in opt)
|
||||
{
|
||||
tmp[0] = source.GetService(propInfo.PropertyType);
|
||||
propInfo.GetSetMethod(true).Invoke(target, tmp);
|
||||
tmp[0] = source.GetService(info.PropType);
|
||||
info.Setter.Invoke(target, tmp);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -44,11 +87,7 @@ namespace BizHawk.Emulation.Common
|
|||
/// and the services provided by the emulator core.
|
||||
/// </summary>
|
||||
public static bool IsAvailable(IEmulatorServiceProvider source, Type targetType)
|
||||
{
|
||||
return targetType.GetPropertiesWithAttrib(typeof(RequiredServiceAttribute))
|
||||
.Select(pi => pi.PropertyType)
|
||||
.All(source.HasService);
|
||||
}
|
||||
=> GetServicePropsFor(targetType, mayCache: false).Req.TrueForAll(info => source.HasService(info.PropType));
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
|
|
Loading…
Reference in New Issue