feat: add fallibility to signature attribute
Also fix delegate function signatures.
This commit is contained in:
parent
214a4c89d5
commit
9f0216ae35
|
@ -0,0 +1,45 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Siggingway;
|
||||
|
||||
internal static class NullabilityUtil {
|
||||
internal static bool IsNullable(PropertyInfo property) => IsNullableHelper(property.PropertyType, property.DeclaringType, property.CustomAttributes);
|
||||
|
||||
internal static bool IsNullable(FieldInfo field) => IsNullableHelper(field.FieldType, field.DeclaringType, field.CustomAttributes);
|
||||
|
||||
internal static bool IsNullable(ParameterInfo parameter) => IsNullableHelper(parameter.ParameterType, parameter.Member, parameter.CustomAttributes);
|
||||
|
||||
private static bool IsNullableHelper(Type memberType, MemberInfo? declaringType, IEnumerable<CustomAttributeData> customAttributes) {
|
||||
if (memberType.IsValueType) {
|
||||
return Nullable.GetUnderlyingType(memberType) != null;
|
||||
}
|
||||
|
||||
var nullable = customAttributes
|
||||
.FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||
if (nullable != null && nullable.ConstructorArguments.Count == 1) {
|
||||
var attributeArgument = nullable.ConstructorArguments[0];
|
||||
if (attributeArgument.ArgumentType == typeof(byte[])) {
|
||||
var args = (ReadOnlyCollection<CustomAttributeTypedArgument>) attributeArgument.Value!;
|
||||
if (args.Count > 0 && args[0].ArgumentType == typeof(byte)) {
|
||||
return (byte) args[0].Value! == 2;
|
||||
}
|
||||
} else if (attributeArgument.ArgumentType == typeof(byte)) {
|
||||
return (byte) attributeArgument.Value! == 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (var type = declaringType; type != null; type = type.DeclaringType) {
|
||||
var context = type.CustomAttributes
|
||||
.FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
|
||||
if (context != null &&
|
||||
context.ConstructorArguments.Count == 1 &&
|
||||
context.ConstructorArguments[0].ArgumentType == typeof(byte)) {
|
||||
return (byte) context.ConstructorArguments[0].Value! == 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find a suitable attribute
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -27,38 +27,54 @@ public static class Siggingway {
|
|||
.Where(field => field.Item2 != null);
|
||||
|
||||
foreach (var (info, sig) in fields) {
|
||||
var wasWrapped = false;
|
||||
var actualType = info.ActualType;
|
||||
if (actualType.IsGenericType && actualType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
|
||||
// unwrap the nullable
|
||||
actualType = actualType.GetGenericArguments()[0];
|
||||
wasWrapped = true;
|
||||
}
|
||||
|
||||
var fallible = sig!.Fallible ?? info.IsNullable || wasWrapped;
|
||||
|
||||
void Invalid(string message, bool prepend = true) {
|
||||
var errorMsg = prepend
|
||||
? $"Invalid Signature attribute for {selfType.FullName}.{info.Name}: {message}"
|
||||
: message;
|
||||
if (fallible) {
|
||||
PluginLog.Warning(errorMsg);
|
||||
} else {
|
||||
throw new SignatureException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
IntPtr ptr;
|
||||
var success = sig!.ScanType == ScanType.Text
|
||||
var success = sig.ScanType == ScanType.Text
|
||||
? scanner.TryScanText(sig.Signature, out ptr)
|
||||
: scanner.TryGetStaticAddressFromSig(sig.Signature, out ptr);
|
||||
if (!success) {
|
||||
if (log) {
|
||||
PluginLog.Warning($"Failed to find {sig.ScanType} signature \"{info.Name}\" for {selfType.FullName} ({sig.Signature})");
|
||||
Invalid($"Failed to find {sig.ScanType} signature \"{info.Name}\" for {selfType.FullName} ({sig.Signature})", false);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
void LogInvalid(string message) {
|
||||
PluginLog.Warning($"Invalid Signature attribute for {selfType.FullName}.{info.Name}: {message}");
|
||||
}
|
||||
|
||||
var actualType = info.ActualType;
|
||||
if (actualType.IsGenericType && actualType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
|
||||
// unwrap the nullable
|
||||
actualType = actualType.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
switch (sig.UseFlags) {
|
||||
case SignatureUseFlags.Auto when actualType == typeof(IntPtr) || actualType.IsPointer || actualType.IsAssignableTo(typeof(Delegate)):
|
||||
case SignatureUseFlags.Pointer: {
|
||||
info.SetValue(self, ptr);
|
||||
if (actualType.IsAssignableTo(typeof(Delegate))) {
|
||||
info.SetValue(self, Marshal.GetDelegateForFunctionPointer(ptr, actualType));
|
||||
} else {
|
||||
info.SetValue(self, ptr);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SignatureUseFlags.Auto when actualType.IsGenericType && actualType.GetGenericTypeDefinition() == typeof(Hook<>):
|
||||
case SignatureUseFlags.Hook: {
|
||||
if (!actualType.IsGenericType || actualType.GetGenericTypeDefinition() != typeof(Hook<>)) {
|
||||
LogInvalid($"{actualType.Name} is not a Hook<T>");
|
||||
Invalid($"{actualType.Name} is not a Hook<T>");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -73,7 +89,7 @@ public static class Siggingway {
|
|||
.Where(del => del != null)
|
||||
.ToArray();
|
||||
if (matches.Length != 1) {
|
||||
LogInvalid("Either found no matching detours or found more than one: specify a detour name");
|
||||
Invalid("Either found no matching detours or found more than one: specify a detour name");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -81,7 +97,7 @@ public static class Siggingway {
|
|||
} else {
|
||||
var method = selfType.GetMethod(sig.DetourName, Flags);
|
||||
if (method == null) {
|
||||
LogInvalid($"Could not find detour \"{sig.DetourName}\"");
|
||||
Invalid($"Could not find detour \"{sig.DetourName}\"");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -89,7 +105,7 @@ public static class Siggingway {
|
|||
? Delegate.CreateDelegate(hookDelegateType, method, false)
|
||||
: Delegate.CreateDelegate(hookDelegateType, self, method, false);
|
||||
if (del == null) {
|
||||
LogInvalid($"Method {sig.DetourName} was not compatible with delegate {hookDelegateType.Name}");
|
||||
Invalid($"Method {sig.DetourName} was not compatible with delegate {hookDelegateType.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -116,7 +132,7 @@ public static class Siggingway {
|
|||
}
|
||||
default: {
|
||||
if (log) {
|
||||
LogInvalid("could not detect desired signature use, set SignatureUseFlags manually");
|
||||
Invalid("could not detect desired signature use, set SignatureUseFlags manually");
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -46,6 +46,15 @@ public sealed class SignatureAttribute : Attribute {
|
|||
/// </summary>
|
||||
public int Offset;
|
||||
|
||||
/// <summary>
|
||||
/// When a signature is fallible, any errors while resolving it will be
|
||||
/// logged in the Dalamud log and the field/property will not have its value
|
||||
/// set. When a signature is not fallible, any errors will be thrown as
|
||||
/// exceptions instead. If fallibility is not specified, it is inferred
|
||||
/// based on if the field/property is nullable.
|
||||
/// </summary>
|
||||
public bool? Fallible;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="SignatureAttribute"/> with the given signature.
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace Siggingway;
|
||||
|
||||
/// <summary>
|
||||
/// An exception for signatures.
|
||||
/// </summary>
|
||||
public class SignatureException : Exception {
|
||||
internal SignatureException(string message) : base(message) {
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ internal interface IFieldOrPropertyInfo {
|
|||
|
||||
Type ActualType { get; }
|
||||
|
||||
bool IsNullable { get; }
|
||||
|
||||
void SetValue(object? self, object? value);
|
||||
|
||||
T? GetCustomAttribute<T>() where T : Attribute;
|
||||
|
@ -23,6 +25,8 @@ internal sealed class FieldInfoWrapper : IFieldOrPropertyInfo {
|
|||
|
||||
public Type ActualType => this.Info.FieldType;
|
||||
|
||||
public bool IsNullable => NullabilityUtil.IsNullable(this.Info);
|
||||
|
||||
public void SetValue(object? self, object? value) {
|
||||
this.Info.SetValue(self, value);
|
||||
}
|
||||
|
@ -43,6 +47,8 @@ internal sealed class PropertyInfoWrapper : IFieldOrPropertyInfo {
|
|||
|
||||
public Type ActualType => this.Info.PropertyType;
|
||||
|
||||
public bool IsNullable => NullabilityUtil.IsNullable(this.Info);
|
||||
|
||||
public void SetValue(object? self, object? value) {
|
||||
this.Info.SetValue(self, value);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue