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

Adds support for targeting an async method's internal MoveNext method #514

Merged
merged 2 commits into from
Mar 22, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Harmony/Internal/PatchTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,16 @@ internal static MethodBase GetOriginalMethod(this HarmonyMethod attr)
case MethodType.Enumerator:
if (attr.methodName is null)
return null;
var method = AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
return AccessTools.EnumeratorMoveNext(method);
var enumMethod = AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
return AccessTools.EnumeratorMoveNext(enumMethod);

#if NET45_OR_GREATER
case MethodType.Async:
if (attr.methodName is null)
return null;
var asyncMethod = AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
return AccessTools.AsyncMoveNext(asyncMethod);
#endif
}
}
catch (AmbiguousMatchException ex)
Expand Down
8 changes: 6 additions & 2 deletions Harmony/Public/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ public enum MethodType
Constructor,
/// <summary>This is a static constructor</summary>
StaticConstructor,
/// <summary>This targets the MoveNext method of the enumerator result</summary>
Enumerator
/// <summary>This targets the MoveNext method of the enumerator result, that actually contains the method's implementation</summary>
Enumerator,
#if NET45_OR_GREATER
/// <summary>This targets the MoveNext method of the async state machine, that actually contains the method's implementation</summary>
Async
#endif
}

/// <summary>Specifies the type of argument</summary>
Expand Down
36 changes: 34 additions & 2 deletions Harmony/Tools/AccessTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Runtime.CompilerServices;
using System.Threading;

namespace HarmonyLib
Expand Down Expand Up @@ -466,9 +467,9 @@ public static MethodInfo Method(string typeColonName, Type[] parameters = null,
return Method(info.type, info.name, parameters, generics);
}

/// <summary>Gets the <see cref="IEnumerator.MoveNext" /> method of an enumerator method</summary>
/// <summary>Gets the <see cref="IEnumerator.MoveNext"/> method of an enumerator method</summary>
/// <param name="method">Enumerator method that creates the enumerator <see cref="IEnumerator" /></param>
/// <returns>The internal <see cref="IEnumerator.MoveNext" /> method of the enumerator or <b>null</b> if no valid enumerator is detected</returns>
/// <returns>The internal <see cref="IEnumerator.MoveNext"/> method of the enumerator or <b>null</b> if no valid enumerator is detected</returns>
public static MethodInfo EnumeratorMoveNext(MethodBase method)
{
if (method is null)
Expand Down Expand Up @@ -498,6 +499,37 @@ public static MethodInfo EnumeratorMoveNext(MethodBase method)
return Method(type, nameof(IEnumerator.MoveNext));
}

#if NET45_OR_GREATER
/// <summary>Gets the <see cref="IAsyncStateMachine.MoveNext"/> method of an async method's state machine</summary>
/// <param name="method">Async method that creates the state machine internally</param>
/// <returns>The internal <see cref="IAsyncStateMachine.MoveNext"/> method of the async state machine or <b>null</b> if no valid async method is detected</returns>
public static MethodInfo AsyncMoveNext(MethodBase method)
{
if (method is null)
{
FileLog.Debug("AccessTools.AsyncMoveNext: method is null");
return null;
}

var asyncAttribute = method.GetCustomAttribute<AsyncStateMachineAttribute>();
if (asyncAttribute is null)
{
FileLog.Debug($"AccessTools.AsyncMoveNext: Could not find AsyncStateMachine for {method.FullDescription()}");
return null;
}

var asyncStateMachineType = asyncAttribute.StateMachineType;
var asyncMethodBody = DeclaredMethod(asyncStateMachineType, nameof(IAsyncStateMachine.MoveNext));
if (asyncMethodBody is null)
{
FileLog.Debug($"AccessTools.AsyncMoveNext: Could not find async method body for {method.FullDescription()}");
return null;
}

return asyncMethodBody;
}
#endif

/// <summary>Gets the names of all method that are declared in a type</summary>
/// <param name="type">The declaring class/type</param>
/// <returns>A list of method names</returns>
Expand Down