Skip to content

Commit 0965267

Browse files
committed
Warn on get-only [RequiredService] (and opt.) props at runtime
previously `ServiceInjector.UpdateServices` would throw NRE an Analyzer would be better but eh
1 parent 829d117 commit 0965267

File tree

1 file changed

+20
-10
lines changed

1 file changed

+20
-10
lines changed

src/BizHawk.Emulation.Common/ServiceInjector.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using System.Reflection;
33

4+
using BizHawk.Common;
45
using BizHawk.Common.ReflectionExtensions;
56

67
namespace BizHawk.Emulation.Common
@@ -14,9 +15,9 @@ private readonly struct ServicePropInfo
1415
{
1516
public readonly Type PropType;
1617

17-
public readonly MethodInfo Setter;
18+
public readonly MethodInfo? Setter;
1819

19-
public ServicePropInfo(Type propType, MethodInfo setter)
20+
public ServicePropInfo(Type propType, MethodInfo? setter)
2021
{
2122
PropType = propType;
2223
Setter = setter;
@@ -29,18 +30,26 @@ private static (List<ServicePropInfo> Req, List<ServicePropInfo> Opt) GetService
2930
this Type @class,
3031
bool mayCache)
3132
{
33+
const string ERR_FMT_STR_GETONLY_PROP = "prop `[{0}] {1}.{2}` is get-only";
3234
if (_cache.TryGetValue(@class, out var pair)) return pair;
3335
pair = (new(), new());
3436
foreach (var pi in @class.GetProperties(ReflectionExtensions.DI_TARGET_PROPS))
3537
{
36-
//TODO enumerate attrs only once
37-
if (pi.GetCustomAttributes(typeof(RequiredServiceAttribute), inherit: false).Length is not 0)
38+
foreach (var attr in pi.GetCustomAttributes())
3839
{
39-
pair.Req.Add(new(pi.PropertyType, pi.GetSetMethod(nonPublic: true)));
40-
}
41-
else if (pi.GetCustomAttributes(typeof(OptionalServiceAttribute), inherit: false).Length is not 0)
42-
{
43-
pair.Opt.Add(new(pi.PropertyType, pi.GetSetMethod(nonPublic: true)));
40+
if (attr is RequiredServiceAttribute)
41+
{
42+
var setter = pi.GetSetMethod(nonPublic: true);
43+
if (setter is null) Util.DebugWriteLine(ERR_FMT_STR_GETONLY_PROP, "RequiredService", @class.Name, pi.Name);
44+
pair.Req.Add(new(pi.PropertyType, setter)); // pass through anyway, and `UpdateServices` will see it and `return false;`
45+
break;
46+
}
47+
else if (attr is OptionalServiceAttribute)
48+
{
49+
if (pi.GetSetMethod(nonPublic: true) is MethodInfo setter) pair.Req.Add(new(pi.PropertyType, setter));
50+
else Util.DebugWriteLine(ERR_FMT_STR_GETONLY_PROP, "OptionalService", @class.Name, pi.Name);
51+
break;
52+
}
4453
}
4554
}
4655
if (mayCache) _cache[@class] = pair;
@@ -69,12 +78,13 @@ public static bool UpdateServices(IEmulatorServiceProvider source, object target
6978
{
7079
return false;
7180
}
81+
if (info.Setter is null) return false;
7282
info.Setter.Invoke(target, tmp);
7383
}
7484
foreach (var info in opt)
7585
{
7686
tmp[0] = source.GetService(info.PropType);
77-
info.Setter.Invoke(target, tmp);
87+
info.Setter!.Invoke(target, tmp);
7888
}
7989

8090
return true;

0 commit comments

Comments
 (0)