Skip to content

Commit

Permalink
Merge pull request #519 from Banane9/master
Browse files Browse the repository at this point in the history
Add targeting of indexer properties
  • Loading branch information
pardeike authored Apr 3, 2023
2 parents e0f9477 + 57ba000 commit fc19016
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
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

0 comments on commit fc19016

Please sign in to comment.