Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add targeting of indexer properties #519

Merged
merged 2 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions Harmony/Internal/PatchTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal static class PatchTools
// https://stackoverflow.com/a/33153868
// ThreadStatic has pitfalls (see RememberObject below), but since we must support net35, it's the best available option.
[ThreadStatic]
static Dictionary<object, object> objectReferences;
private static Dictionary<object, object> objectReferences;

internal static void RememberObject(object key, object value)
{
Expand Down Expand Up @@ -65,12 +65,12 @@ internal static MethodBase GetOriginalMethod(this HarmonyMethod attr)

case MethodType.Getter:
if (attr.methodName is null)
return null;
return AccessTools.DeclaredIndexer(attr.declaringType, attr.argumentTypes).GetGetMethod(true);
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true);

case MethodType.Setter:
if (attr.methodName is null)
return null;
return AccessTools.DeclaredIndexer(attr.declaringType, attr.argumentTypes).GetSetMethod(true);
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true);

case MethodType.Constructor:
Expand Down
100 changes: 100 additions & 0 deletions Harmony/Tools/AccessTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,36 @@ public static PropertyInfo DeclaredProperty(string typeColonName)
return property;
}

/// <summary>Gets the reflection information for a directly declared indexer property</summary>
/// <param name="type">The class/type where the indexer property is declared</param>
/// <param name="parameters">Optional parameters to target a specific overload of multiple indexers</param>
/// <returns>An indexer property or null when type is null or when it cannot be found</returns>
///
public static PropertyInfo DeclaredIndexer(Type type, Type[] parameters = null)
{
if (type is null)
{
FileLog.Debug("AccessTools.DeclaredIndexer: type is null");
return null;
}

try
{
// Can find multiple indexers without specified parameters, but only one with specified ones
var indexer = parameters is null ?
type.GetProperties(allDeclared).SingleOrDefault(property => property.GetIndexParameters().Any())
: type.GetProperties(allDeclared).FirstOrDefault(property => property.GetIndexParameters().Select(param => param.ParameterType).SequenceEqual(parameters));

if (indexer is null) FileLog.Debug($"AccessTools.DeclaredIndexer: Could not find indexer for type {type} and parameters {parameters?.Description()}");

return indexer;
}
catch (InvalidOperationException ex)
{
throw new AmbiguousMatchException("Multiple possible indexers were found.", ex);
}
}

/// <summary>Gets the reflection information for the getter method of a directly declared property</summary>
/// <param name="type">The class/type where the property is declared</param>
/// <param name="name">The name of the property (case sensitive)</param>
Expand All @@ -264,6 +294,16 @@ public static MethodInfo DeclaredPropertyGetter(string typeColonName)
return DeclaredProperty(typeColonName)?.GetGetMethod(true);
}

/// <summary>Gets the reflection information for the getter method of a directly declared indexer property</summary>
/// <param name="type">The class/type where the indexer property is declared</param>
/// <param name="parameters">Optional parameters to target a specific overload of multiple indexers</param>
/// <returns>A method or null when type is null or when indexer property cannot be found</returns>
///
public static MethodInfo DeclaredIndexerGetter(Type type, Type[] parameters = null)
{
return DeclaredIndexer(type, parameters)?.GetGetMethod(true);
}

/// <summary>Gets the reflection information for the setter method of a directly declared property</summary>
/// <param name="type">The class/type where the property is declared</param>
/// <param name="name">The name of the property (case sensitive)</param>
Expand All @@ -283,6 +323,16 @@ public static MethodInfo DeclaredPropertySetter(string typeColonName)
return DeclaredProperty(typeColonName)?.GetSetMethod(true);
}

/// <summary>Gets the reflection information for the setter method of a directly declared indexer property</summary>
/// <param name="type">The class/type where the indexer property is declared</param>
/// <param name="parameters">Optional parameters to target a specific overload of multiple indexers</param>
/// <returns>A method or null when type is null or when indexer property cannot be found</returns>
///
public static MethodInfo DeclaredIndexerSetter(Type type, Type[] parameters)
{
return DeclaredIndexer(type, parameters)?.GetSetMethod(true);
}

/// <summary>Gets the reflection information for a property by searching the type and all its super types</summary>
/// <param name="type">The class/type</param>
/// <param name="name">The name</param>
Expand Down Expand Up @@ -317,6 +367,38 @@ public static PropertyInfo Property(string typeColonName)
return property;
}

/// <summary>Gets the reflection information for an indexer property by searching the type and all its super types</summary>
/// <param name="type">The class/type</param>
/// <param name="parameters">Optional parameters to target a specific overload of multiple indexers</param>
/// <returns>An indexer property or null when type is null or when it cannot be found</returns>
///
public static PropertyInfo Indexer(Type type, Type[] parameters = null)
{
if (type is null)
{
FileLog.Debug("AccessTools.Indexer: type is null");
return null;
}

// Can find multiple indexers without specified parameters, but only one with specified ones
Func<Type, PropertyInfo> func = parameters is null ?
t => t.GetProperties(all).SingleOrDefault(property => property.GetIndexParameters().Any())
: t => t.GetProperties(all).FirstOrDefault(property => property.GetIndexParameters().Select(param => param.ParameterType).SequenceEqual(parameters));

try
{
var indexer = FindIncludingBaseTypes(type, func);

if (indexer is null) FileLog.Debug($"AccessTools.Indexer: Could not find indexer for type {type} and parameters {parameters?.Description()}");

return indexer;
}
catch (InvalidOperationException ex)
{
throw new AmbiguousMatchException("Multiple possible indexers were found.", ex);
}
}

/// <summary>Gets the reflection information for the getter method of a property by searching the type and all its super types</summary>
/// <param name="type">The class/type</param>
/// <param name="name">The name</param>
Expand All @@ -336,6 +418,15 @@ public static MethodInfo PropertyGetter(string typeColonName)
return Property(typeColonName)?.GetGetMethod(true);
}

/// <summary>Gets the reflection information for the getter method of an indexer property by searching the type and all its super types</summary>
/// <param name="type">The class/type</param>
/// <param name="parameters">Optional parameters to target a specific overload of multiple indexers</param>
/// <returns>A method or null when type is null or when the indexer property cannot be found</returns>
public static MethodInfo IndexerGetter(Type type, Type[] parameters = null)
{
return Indexer(type, parameters)?.GetGetMethod(true);
}

/// <summary>Gets the reflection information for the setter method of a property by searching the type and all its super types</summary>
/// <param name="type">The class/type</param>
/// <param name="name">The name</param>
Expand All @@ -355,6 +446,15 @@ public static MethodInfo PropertySetter(string typeColonName)
return Property(typeColonName)?.GetSetMethod(true);
}

/// <summary>Gets the reflection information for the setter method of an indexer property by searching the type and all its super types</summary>
/// <param name="type">The class/type</param>
/// <param name="parameters">Optional parameters to target a specific overload of multiple indexers</param>
/// <returns>A method or null when type is null or when the indexer property cannot be found</returns>
public static MethodInfo IndexerSetter(Type type, Type[] parameters = null)
{
return Indexer(type, parameters)?.GetSetMethod(true);
}

/// <summary>Gets the reflection information for a directly declared method</summary>
/// <param name="type">The class/type where the method is declared</param>
/// <param name="name">The name of the method (case sensitive)</param>
Expand Down