Skip to content

Commit af69e0f

Browse files
CptMooreManlyMarco
authored andcommitted
Added unit test for extern instance method native detour patching.
1 parent c96ca55 commit af69e0f

File tree

2 files changed

+109
-8
lines changed

2 files changed

+109
-8
lines changed

HarmonyTests/Patching/Assets/NativeDetourClasses.cs

+48-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,54 @@
55

66
namespace HarmonyTests.Patching.Assets;
77

8-
public class ExternalMethod_Patch
8+
public class ExternalInstanceMethod_StringIsInterned_Patch
9+
{
10+
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) =>
11+
instructions;
12+
13+
private const string UniqueString = nameof(ExternalInstanceMethod_StringIsInterned_Patch);
14+
15+
public const string PrefixInput = $"{UniqueString} {nameof(PrefixInput)}";
16+
public const string PrefixOutput = $"{UniqueString} {nameof(PrefixOutput)}";
17+
public static void Prefix(ref string __instance, ref string __result, ref bool __runOriginal)
18+
{
19+
if (__instance == PrefixInput)
20+
{
21+
__result = PrefixOutput;
22+
__runOriginal = false;
23+
}
24+
}
25+
26+
public const string PostfixInput = $"{UniqueString} {nameof(PostfixInput)}";
27+
public const string PostfixOutput = $"{UniqueString} {nameof(PostfixOutput)}";
28+
public static void Postfix(ref string __instance, ref string __result)
29+
{
30+
if (__instance == PostfixInput)
31+
{
32+
__result = PostfixOutput;
33+
}
34+
}
35+
36+
public static readonly Type TranspiledException = typeof(UnauthorizedAccessException);
37+
public static IEnumerable<CodeInstruction> TranspilerThrow(IEnumerable<CodeInstruction> instructions)
38+
{
39+
yield return new CodeInstruction(OpCodes.Newobj, TranspiledException.GetConstructor([]));
40+
yield return new CodeInstruction(OpCodes.Throw);
41+
}
42+
43+
public const string FinalizerInput = $"{UniqueString} {nameof(FinalizerInput)}";
44+
public const string FinalizerOutput = $"{UniqueString} {nameof(FinalizerOutput)}";
45+
public static Exception Finalizer(ref string __instance, ref string __result)
46+
{
47+
if (__instance == FinalizerInput)
48+
{
49+
__result = FinalizerOutput;
50+
}
51+
return null;
52+
}
53+
}
54+
55+
public class ExternalStaticMethod_MathCos_Patch
956
{
1057
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) =>
1158
instructions;

HarmonyTests/Patching/NativeDetourPatches.cs

+61-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,68 @@
22
using HarmonyTests.Patching.Assets;
33
using NUnit.Framework;
44
using System;
5+
using System.IO;
6+
using System.Reflection;
7+
using System.Text;
58

69
namespace HarmonyLibTests.Patching;
710

11+
using EIP = ExternalInstanceMethod_StringIsInterned_Patch;
12+
813
[TestFixture]
914
public class NativeDetourPatches : TestLogger
1015
{
1116
[Test]
12-
public void Test_PatchExternalMethod()
17+
public void Test_PatchInstanceExternalMethod()
18+
{
19+
var target = typeof(string).GetMethod("Intern", BindingFlags.Instance|BindingFlags.NonPublic);
20+
21+
if(target == null)
22+
Assert.Inconclusive("string.Intern is missing in current runtime");
23+
24+
#if !NET35
25+
if((target.MethodImplementationFlags & MethodImplAttributes.InternalCall) == 0)
26+
Assert.Inconclusive("string.Intern is not an InternalCall (extern) in current runtime ");
27+
#endif
28+
29+
if(target.GetMethodBody() != null)
30+
Assert.Inconclusive("string.Intern has IL body in current runtime");
31+
32+
var str1 = new StringBuilder().Append('o').Append('k').Append('4').Append('1').ToString();
33+
Assert.IsNull(string.IsInterned(str1));
34+
var internedStr1 = string.Intern(str1);
35+
Assert.AreEqual(internedStr1, string.IsInterned(str1));
36+
37+
var instance = new Harmony("test-patch-external-instance-method");
38+
Assert.NotNull(instance, "Harmony instance");
39+
40+
instance.Patch(target, transpiler: typeof(EIP).Method("Transpiler"));
41+
var str2 = new StringBuilder().Append('o').Append('k').Append('4').Append('2').ToString();
42+
Assert.IsNull(string.IsInterned(str2));
43+
var internedStr2 = string.Intern(str2);
44+
Assert.AreEqual(internedStr2, string.IsInterned(str2));
45+
46+
instance.Patch(target, prefix: typeof(EIP).Method("Prefix"));
47+
Assert.AreEqual(EIP.PrefixOutput, string.Intern(EIP.PrefixInput));
48+
49+
instance.Patch(target, postfix: typeof(EIP).Method("Postfix"));
50+
Assert.AreEqual(EIP.PostfixOutput, string.Intern(EIP.PostfixInput));
51+
52+
instance.Patch(target, transpiler: typeof(EIP).Method("TranspilerThrow"));
53+
Assert.Throws(EIP.TranspiledException, () => string.Intern("does not matter"));
54+
55+
instance.Patch(target, finalizer: typeof(EIP).Method("Finalizer"));
56+
Assert.AreEqual(EIP.FinalizerOutput, string.Intern(EIP.FinalizerInput));
57+
58+
instance.UnpatchSelf();
59+
var str3 = new StringBuilder().Append('o').Append('k').Append('4').Append('3').ToString();
60+
Assert.IsNull(string.IsInterned(str3));
61+
Assert.AreEqual(internedStr1, string.IsInterned(str1));
62+
Assert.AreEqual(internedStr2, string.IsInterned(str2));
63+
}
64+
65+
[Test]
66+
public void Test_PatchStaticExternalMethod()
1367
{
1468
var target = SymbolExtensions.GetMethodInfo(() => Math.Cos(0));
1569

@@ -20,22 +74,22 @@ public void Test_PatchExternalMethod()
2074
var cos = Math.Cos;
2175
Assert.AreEqual(1d, cos(0d));
2276

23-
var instance = new Harmony("test-patch-external-method");
77+
var instance = new Harmony("test-patch-external-static-method");
2478
Assert.NotNull(instance, "Harmony instance");
2579

26-
instance.Patch(target, transpiler: typeof(ExternalMethod_Patch).Method("Transpiler"));
80+
instance.Patch(target, transpiler: typeof(ExternalStaticMethod_MathCos_Patch).Method("Transpiler"));
2781
Assert.AreEqual(1d, cos(0d));
2882

29-
instance.Patch(target, prefix: typeof(ExternalMethod_Patch).Method("Prefix"));
83+
instance.Patch(target, prefix: typeof(ExternalStaticMethod_MathCos_Patch).Method("Prefix"));
3084
Assert.AreEqual(1d, cos(2d));
3185

32-
instance.Patch(target, postfix: typeof(ExternalMethod_Patch).Method("Postfix"));
86+
instance.Patch(target, postfix: typeof(ExternalStaticMethod_MathCos_Patch).Method("Postfix"));
3387
Assert.AreEqual(2d, cos(0d));
3488

35-
instance.Patch(target, transpiler: typeof(ExternalMethod_Patch).Method("TranspilerThrow"));
89+
instance.Patch(target, transpiler: typeof(ExternalStaticMethod_MathCos_Patch).Method("TranspilerThrow"));
3690
Assert.Throws<UnauthorizedAccessException>(() => cos(0d));
3791

38-
instance.Patch(target, finalizer: typeof(ExternalMethod_Patch).Method("Finalizer"));
92+
instance.Patch(target, finalizer: typeof(ExternalStaticMethod_MathCos_Patch).Method("Finalizer"));
3993
Assert.AreEqual(-2d, cos(0d));
4094

4195
instance.UnpatchSelf();

0 commit comments

Comments
 (0)