Enable nullable reference types in BizInvoke

This commit is contained in:
YoshiRulz 2021-03-26 11:38:38 +10:00
parent 23d8417ca8
commit b6f1afcce8
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
7 changed files with 67 additions and 57 deletions

View File

@ -71,10 +71,10 @@ namespace BizHawk.BizInvoke
foreach (var a in methods)
{
MethodBuilder unused;
var delegateType = BizInvokeUtilities.CreateDelegateType(a.Info, a.Attr.CallingConvention, typeBuilder, out unused).CreateType();
var delegateType = BizInvokeUtilities.CreateDelegateType(a.Info, a.Attr!.CallingConvention, typeBuilder, out unused).CreateType()!;
DelegateTypes.Add(new StoredDelegateInfo(a.Info, delegateType, a.Attr.EntryPoint ?? a.Info.Name));
}
StorageType = typeBuilder.CreateType();
StorageType = typeBuilder.CreateType()!;
OriginalType = type;
}
}
@ -105,18 +105,18 @@ namespace BizHawk.BizInvoke
public static IImportResolver GetExvoker(object o, ICallingConventionAdapter a)
{
DelegateStorage ds;
DelegateStorage? ds;
lock (Impls)
{
var type = o.GetType();
if (!Impls.TryGetValue(type, out ds))
{
ds = new DelegateStorage(type);
Impls.Add(type, ds);
Impls.Add(type, ds!);
}
}
return new ExvokerImpl(o, ds, a);
return new ExvokerImpl(o, ds!, a);
}
}
@ -127,7 +127,7 @@ namespace BizHawk.BizInvoke
public CallingConvention CallingConvention { get; }
/// <remarks>The annotated method's name is used iff <see langword="null"/>.</remarks>
public string EntryPoint { get; set; }
public string? EntryPoint { get; set; } = null;
public BizExportAttribute(CallingConvention c)
{

View File

@ -5,7 +5,6 @@
<Import Project="../MainSlnCommon.props" />
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(ProjectDir)../BizHawk.Common/BizHawk.Common.csproj" />

View File

@ -54,7 +54,7 @@ namespace BizHawk.BizInvoke
}
{
var p = delegateInvoke.DefineParameter(0, ParameterAttributes.Retval, method.ReturnParameter.Name);
var p = delegateInvoke.DefineParameter(0, ParameterAttributes.Retval, method.ReturnParameter!.Name);
foreach (var a in method.ReturnParameter.GetCustomAttributes(false))
{
p.SetCustomAttribute(GetAttributeBuilder(a));
@ -64,7 +64,9 @@ namespace BizHawk.BizInvoke
delegateInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
// add the [UnmanagedFunctionPointer] to the delegate so interop will know how to call it
var attr = new CustomAttributeBuilder(typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) }), new object[] { nativeCall });
var attr = new CustomAttributeBuilder(
typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) })!,
new object[] { nativeCall });
delegateType.SetCustomAttribute(attr);
invokeMethod = delegateInvoke;
@ -80,7 +82,7 @@ namespace BizHawk.BizInvoke
var t = o.GetType();
if (t == typeof(OutAttribute) || t == typeof(InAttribute))
{
return new CustomAttributeBuilder(t.GetConstructor(Type.EmptyTypes), new object[0]);
return new CustomAttributeBuilder(t.GetConstructor(Type.EmptyTypes)!, new object[0]);
}
throw new InvalidOperationException($"Unknown parameter attribute {t.Name}");

View File

@ -18,7 +18,7 @@ namespace BizHawk.BizInvoke
{
private readonly Action<object, ICallingConventionAdapter> _connectCallingConventionAdapter;
private readonly Action<object, IMonitor> _connectMonitor;
private readonly Action<object, IMonitor>? _connectMonitor;
private readonly List<Action<object, IImportResolver, ICallingConventionAdapter>> _hooks;
@ -29,7 +29,7 @@ namespace BizHawk.BizInvoke
public InvokerImpl(
List<Action<object, IImportResolver, ICallingConventionAdapter>> hooks,
Type implType,
Action<object, IMonitor> connectMonitor,
Action<object, IMonitor>? connectMonitor,
Action<object, ICallingConventionAdapter> connectCallingConventionAdapter)
{
_connectCallingConventionAdapter = connectCallingConventionAdapter;
@ -39,15 +39,15 @@ namespace BizHawk.BizInvoke
IsMonitored = connectMonitor != null;
}
public object Create(IImportResolver dll, IMonitor monitor, ICallingConventionAdapter adapter)
public object Create(IImportResolver dll, IMonitor? monitor, ICallingConventionAdapter adapter)
{
var ret = Activator.CreateInstance(_implType);
var ret = Activator.CreateInstance(_implType)!;
_connectCallingConventionAdapter(ret, adapter);
foreach (var f in _hooks)
{
f(ret, dll, adapter);
}
_connectMonitor?.Invoke(ret, monitor);
_connectMonitor?.Invoke(ret, monitor!);
return ret;
}
}
@ -94,18 +94,18 @@ namespace BizHawk.BizInvoke
where T : class
{
var nonTrivialAdapter = adapter.GetType() != CallingConventionAdapters.Native.GetType();
InvokerImpl impl;
InvokerImpl? impl;
lock (Impls)
{
var baseType = typeof(T);
if (!Impls.TryGetValue(baseType, out impl))
{
impl = CreateProxy(baseType, false, nonTrivialAdapter);
Impls.Add(baseType, impl);
Impls.Add(baseType, impl!);
}
}
if (impl.IsMonitored)
if (impl!.IsMonitored)
{
throw new InvalidOperationException("Class was previously proxied with a monitor!");
}
@ -118,18 +118,18 @@ namespace BizHawk.BizInvoke
where T : class
{
var nonTrivialAdapter = adapter.GetType() != CallingConventionAdapters.Native.GetType();
InvokerImpl impl;
InvokerImpl? impl;
lock (Impls)
{
var baseType = typeof(T);
if (!Impls.TryGetValue(baseType, out impl))
{
impl = CreateProxy(baseType, true, nonTrivialAdapter);
Impls.Add(baseType, impl);
Impls.Add(baseType, impl!);
}
}
if (!impl.IsMonitored)
if (!(impl!.IsMonitored))
{
throw new InvalidOperationException("Class was previously proxied without a monitor!");
}
@ -197,7 +197,7 @@ namespace BizHawk.BizInvoke
foreach (var mi in baseMethods)
{
var entryPointName = mi.Attr.EntryPoint ?? mi.Info.Name;
var entryPointName = mi.Attr!.EntryPoint ?? mi.Info.Name;
var hook = mi.Attr.Compatibility
? ImplementMethodDelegate(type, mi.Info, mi.Attr.CallingConvention, entryPointName, monitorField, nonTrivialAdapter)
@ -208,9 +208,9 @@ namespace BizHawk.BizInvoke
return new(
postCreateHooks,
type.CreateType(),
type.CreateType()!,
connectMonitor: monitor
? (o, m) => o.GetType().GetField(monitorField.Name).SetValue(o, m)
? (o, m) => o.GetType().GetField(monitorField!.Name).SetValue(o, m)
: null,
connectCallingConventionAdapter: (o, a) => o.GetType().GetField(adapterField.Name).SetValue(o, a));
}
@ -219,7 +219,11 @@ namespace BizHawk.BizInvoke
/// create a method implementation that uses GetDelegateForFunctionPointer internally
/// </summary>
private static Action<object, IImportResolver, ICallingConventionAdapter> ImplementMethodDelegate(
TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField,
TypeBuilder type,
MethodInfo baseMethod,
CallingConvention nativeCall,
string entryPointName,
FieldInfo? monitorField,
bool nonTrivialAdapter)
{
// create the delegate type
@ -260,7 +264,7 @@ namespace BizHawk.BizInvoke
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, monitorField);
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Enter"));
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Enter")!);
exc = il.BeginExceptionBlock();
}
@ -275,7 +279,7 @@ namespace BizHawk.BizInvoke
if (monitorField != null) // monitor: finally exit
{
LocalBuilder loc = null;
LocalBuilder? loc = null;
if (returnType != typeof(void))
{
loc = il.DeclareLocal(returnType);
@ -286,12 +290,12 @@ namespace BizHawk.BizInvoke
il.BeginFinallyBlock();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, monitorField);
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Exit"));
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Exit")!);
il.EndExceptionBlock();
if (returnType != typeof(void))
{
il.Emit(OpCodes.Ldloc, loc);
il.Emit(OpCodes.Ldloc, loc!);
}
}
@ -331,8 +335,12 @@ namespace BizHawk.BizInvoke
/// create a method implementation that uses calli internally
/// </summary>
private static Action<object, IImportResolver, ICallingConventionAdapter> ImplementMethodCalli(
TypeBuilder type, MethodInfo baseMethod,
CallingConvention nativeCall, string entryPointName, FieldInfo monitorField, FieldInfo adapterField)
TypeBuilder type,
MethodInfo baseMethod,
CallingConvention nativeCall,
string entryPointName,
FieldInfo? monitorField,
FieldInfo adapterField)
{
var paramInfos = baseMethod.GetParameters();
var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
@ -363,7 +371,7 @@ namespace BizHawk.BizInvoke
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, monitorField);
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Enter"));
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Enter")!);
exc = il.BeginExceptionBlock();
}
@ -388,7 +396,7 @@ namespace BizHawk.BizInvoke
if (monitorField != null) // monitor: finally exit
{
LocalBuilder loc = null;
LocalBuilder? loc = null;
if (returnType != typeof(void))
{
loc = il.DeclareLocal(returnType);
@ -399,12 +407,12 @@ namespace BizHawk.BizInvoke
il.BeginFinallyBlock();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, monitorField);
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Exit"));
il.Emit(OpCodes.Callvirt, typeof(IMonitor).GetMethod("Exit")!);
il.EndExceptionBlock();
if (returnType != typeof(void))
{
il.Emit(OpCodes.Ldloc, loc);
il.Emit(OpCodes.Ldloc, loc!);
}
}
@ -477,7 +485,7 @@ namespace BizHawk.BizInvoke
if (type.IsByRef)
{
var et = type.GetElementType();
var et = type.GetElementType()!;
if (!et.IsPrimitive && !et.IsEnum)
{
throw new NotImplementedException("Only refs of primitive or enum types are supported!");
@ -496,7 +504,7 @@ namespace BizHawk.BizInvoke
if (type.IsArray)
{
var et = type.GetElementType();
var et = type.GetElementType()!;
if (!et.IsValueType)
{
throw new NotImplementedException("Only arrays of value types are supported!");
@ -545,7 +553,7 @@ namespace BizHawk.BizInvoke
typeof(IntPtr),
() =>
{
var mi = typeof(ICallingConventionAdapter).GetMethod("GetFunctionPointerForDelegate");
var mi = typeof(ICallingConventionAdapter).GetMethod("GetFunctionPointerForDelegate")!;
var end = il.DefineLabel();
var isNull = il.DefineLabel();
@ -573,13 +581,13 @@ namespace BizHawk.BizInvoke
il.Emit(OpCodes.Brfalse, isNull);
var encoding = il.DeclareLocal(typeof(Encoding), false);
il.EmitCall(OpCodes.Call, typeof(Encoding).GetProperty("UTF8").GetGetMethod(), Type.EmptyTypes);
il.EmitCall(OpCodes.Call, typeof(Encoding).GetProperty("UTF8")!.GetGetMethod(), Type.EmptyTypes);
il.Emit(OpCodes.Stloc, encoding);
var strlenbytes = il.DeclareLocal(typeof(int), false);
il.Emit(OpCodes.Ldloc, encoding);
il.Emit(OpCodes.Ldarg, (short)idx);
il.EmitCall(OpCodes.Callvirt, typeof(Encoding).GetMethod("GetByteCount", new[] { typeof(string) }), Type.EmptyTypes);
il.EmitCall(OpCodes.Callvirt, typeof(Encoding).GetMethod("GetByteCount", new[] { typeof(string) })!, Type.EmptyTypes);
il.Emit(OpCodes.Stloc, strlenbytes);
var strval = il.DeclareLocal(typeof(string), true); // pin!
@ -605,13 +613,13 @@ namespace BizHawk.BizInvoke
il.Emit(OpCodes.Add);
// charcount
il.Emit(OpCodes.Ldloc, strval);
il.Emit(OpCodes.Call, typeof(string).GetProperty("Length").GetGetMethod());
il.Emit(OpCodes.Call, typeof(string).GetProperty("Length")!.GetGetMethod());
// bytes
il.Emit(OpCodes.Ldloc, bytes);
// bytelength
il.Emit(OpCodes.Ldloc, strlenbytes);
// call
il.EmitCall(OpCodes.Callvirt, typeof(Encoding).GetMethod("GetBytes", new[] { typeof(char*), typeof(int), typeof(byte*), typeof(int) }), Type.EmptyTypes);
il.EmitCall(OpCodes.Callvirt, typeof(Encoding).GetMethod("GetBytes", new[] { typeof(char*), typeof(int), typeof(byte*), typeof(int) })!, Type.EmptyTypes);
// unused ret
il.Emit(OpCodes.Pop);
@ -681,7 +689,7 @@ namespace BizHawk.BizInvoke
public CallingConvention CallingConvention { get; }
/// <remarks>The annotated method's name is used iff <see langword="null"/>.</remarks>
public string EntryPoint { get; set; }
public string? EntryPoint { get; set; } = null;
/// <summary><see langword="true"/> iff a compatibility interop should be used, which is slower but supports more argument types.</summary>
public bool Compatibility { get; set; }

View File

@ -5,6 +5,7 @@ namespace BizHawk.BizInvoke
{
public static unsafe class BizInvokerUtilities
{
#nullable disable //TODO If U1 and U2 were structs, this wouldn't be a problem. Would changing them to structs break this helper's functionality? --yoshi
[StructLayout(LayoutKind.Explicit)]
private class U
{
@ -13,6 +14,7 @@ namespace BizHawk.BizInvoke
[FieldOffset(0)]
public U2 Second;
}
#nullable restore
[StructLayout(LayoutKind.Explicit)]
private class U1
{
@ -24,6 +26,8 @@ namespace BizHawk.BizInvoke
{
[FieldOffset(0)]
public object Target;
public U2(object target) => Target = target;
}
private class CF
{
@ -39,7 +43,7 @@ namespace BizHawk.BizInvoke
int ret;
fixed(int* fx = &c.FirstField)
{
var u = new U { Second = new U2 { Target = c }};
var u = new U { Second = new U2(c) };
ret = (int)((ulong)(UIntPtr)fx - (ulong)u.First.P);
}
return ret;
@ -50,7 +54,7 @@ namespace BizHawk.BizInvoke
int ret;
fixed(char* fx = s)
{
var u = new U { Second = new U2 { Target = s }};
var u = new U { Second = new U2(s) };
ret = (int)((ulong)(UIntPtr)fx - (ulong)u.First.P);
}
return ret;

View File

@ -68,7 +68,7 @@ namespace BizHawk.BizInvoke
{
if (!typeof(Delegate).IsAssignableFrom(delegateType))
throw new InvalidOperationException("Must be a delegate type!");
var invoke = delegateType.GetMethod("Invoke");
var invoke = delegateType.GetMethod("Invoke")!;
ReturnType = invoke.ReturnType;
ParameterTypes = invoke.GetParameters().Select(p => p.ParameterType).ToList().AsReadOnly();
}
@ -170,10 +170,10 @@ namespace BizHawk.BizInvoke
: (ICallingConventionAdapter)new MsHostSysVGuest();
}
private readonly Dictionary<Delegate, int> _slots;
private readonly Dictionary<Delegate, int>? _slots;
private readonly ICallbackAdjuster _waterboxHost;
public WaterboxAdapter(IEnumerable<Delegate> slots, ICallbackAdjuster waterboxHost)
public WaterboxAdapter(IEnumerable<Delegate>? slots, ICallbackAdjuster waterboxHost)
{
if (slots != null)
{
@ -246,7 +246,7 @@ namespace BizHawk.BizInvoke
private readonly MemoryBlock _memory;
private readonly object _sync = new object();
private readonly WeakReference[] _refs;
private readonly WeakReference?[] _refs;
public MsHostSysVGuest()
{
@ -259,8 +259,7 @@ namespace BizHawk.BizInvoke
{
for (int i = 0; i < _refs.Length; i++)
{
if (_refs[i] == null || !_refs[i].IsAlive)
return i;
if (_refs[i] is not { IsAlive: true }) return i; // return index of first null or dead
}
throw new InvalidOperationException("Out of Thunk memory");
}
@ -331,10 +330,8 @@ namespace BizHawk.BizInvoke
private void SetLifetime(int index, object lifetime)
{
if (_refs[index] == null)
_refs[index] = new WeakReference(lifetime);
else
_refs[index].Target = lifetime;
_refs[index] ??= new(null);
_refs[index]!.Target = lifetime;
}
public IntPtr GetFunctionPointerForDelegate(Delegate d)

View File

@ -21,11 +21,11 @@ namespace BizHawk.BizInvoke
_pal = OSTailoredCode.IsUnixHost
? (IMemoryBlockPal)new MemoryBlockLinuxPal(Size)
: new MemoryBlockWindowsPal(Size);
Start = _pal.Start;
Start = _pal!.Start;
EndExclusive = Start + Size;
}
private IMemoryBlockPal _pal;
private IMemoryBlockPal? _pal;
/// <summary>
/// end address of the memory block (not part of the block; class invariant: equal to <see cref="Start"/> + <see cref="Size"/>)