Skip to content

Commit 77c7f57

Browse files
committed
Added partial parameter passing, method matching
* InjectionDefiniton now doesn't require injection methods marked with `InjectFlags.PassParametersVal` or `InjectFlags.PassParametersRef` to have **all** of the target method's parameters. The order must still be the same, though (might improve that later). * Added method matching that allows to find methods that have **at least** the specified parameters.
1 parent 6b66378 commit 77c7f57

File tree

4 files changed

+114
-16
lines changed

4 files changed

+114
-16
lines changed

Mono.Cecil.Inject/InjectFlags.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,19 @@ public enum InjectFlags
6868

6969
/// <summary>
7070
/// Pass all parameters of the injected method. The parameters are passed by value.
71+
/// <b>Note:</b> Some methods (like constructor of <see cref="InjectionDefinition" />) implement partial parameter
72+
/// passing.
73+
/// It means that the injection method doesn't need to have all of the parameters for injected method (as long as they
74+
/// are in the same order).
7175
/// </summary>
7276
PassParametersVal = 0x20,
7377

7478
/// <summary>
7579
/// Pass all parameters of the injected method. The parameters are passed by reference (marked as "ref").
80+
/// <b>Note:</b> Some methods (like constructor of <see cref="InjectionDefinition" />) implement partial parameter
81+
/// passing.
82+
/// It means that the injection method doesn't need to have all of the parameters for injected method (as long as they
83+
/// are in the same order).
7684
/// </summary>
7785
PassParametersRef = 0x40,
7886

Mono.Cecil.Inject/InjectionDefinition.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,21 @@ public InjectionDefinition(MethodDefinition injectTarget,
5757
int prefixCount = Convert.ToInt32(hFlags.PassTag) + Convert.ToInt32(hFlags.PassInvokingInstance)
5858
+ Convert.ToInt32(hFlags.ModifyReturn && !isVoid);
5959
int localsCount = hFlags.PassLocals ? localVarIDs.Length : 0;
60-
int paramCount = (hFlags.PassParameters ? injectTarget.Parameters.Count : 0);
6160
int memberRefCount = (hFlags.PassFields ? memberReferences.Length : 0);
62-
61+
int paramCount = (hFlags.PassParameters ? injectTarget.Parameters.Count : 0);
62+
int parameters = injectMethod.Parameters.Count - prefixCount - localsCount - memberRefCount;
63+
// int parameters = injectMethod.Parameters.Count - prefixCount - localsCount - memberRefCount;
64+
// PassParameters => 0 < parameters <= injectTarget.Patameters.Count
65+
// !PassParameters => parameters = 0
6366
Assert(
64-
injectMethod.Parameters.Count == prefixCount + localsCount + memberRefCount + paramCount,
67+
(hFlags.PassParameters && 0 < parameters && parameters <= injectTarget.Parameters.Count
68+
|| !hFlags.PassParameters && parameters == 0),
69+
//injectMethod.Parameters.Count == prefixCount + localsCount + memberRefCount + paramCount,
6570
$@"The injection method has a wrong number of parameters! Check that the provided target method, local variables, member references and injection flags add up to the right number of parameters.
6671
Needed parameters: Prefix: {
67-
prefixCount}, Locals: {localsCount}, Members: {memberRefCount}, Parameters: {paramCount}, TOTAL: {
72+
prefixCount}, Locals: {localsCount}, Members: {memberRefCount}, Parameters: {
73+
(hFlags.PassParameters ? "between 1 and " + paramCount : "0")}, TOTAL: {
74+
(hFlags.PassParameters ? $"between {prefixCount + localsCount + memberRefCount + 1} and " : "")}{
6875
prefixCount + localsCount + memberRefCount + paramCount}.
6976
Injection has {injectMethod.Parameters.Count
7077
} parameters.");
@@ -173,8 +180,11 @@ Injection has {injectMethod.Parameters.Count
173180
injectMethod.Parameters.Skip(prefixCount + localsCount + memberRefCount)
174181
.Select(p => p.ParameterType)
175182
.SequenceEqual(
176-
injectTarget.Parameters.Select(
177-
p => (hFlags.PassParametersByRef ? new ByReferenceType(p.ParameterType) : p.ParameterType)),
183+
injectTarget.Parameters.Take(parameters)
184+
.Select(
185+
p =>
186+
(hFlags.PassParametersByRef
187+
? new ByReferenceType(p.ParameterType) : p.ParameterType)),
178188
comparer),
179189
"Supposed to pass target method parameters by reference, but the types specified in injection and target methods do not match.");
180190
}
@@ -186,7 +196,7 @@ Injection has {injectMethod.Parameters.Count
186196
LocalVarIDs = localVarIDs;
187197
_PrefixCount = prefixCount;
188198
_MemeberRefCount = memberRefCount;
189-
_ParameterCount = injectTarget.Parameters.Count;
199+
_ParameterCount = parameters;
190200
}
191201

192202
internal int _MemeberRefCount { get; set; }

Mono.Cecil.Inject/TypeDefinitionExtensions.cs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,13 @@ public static MethodDefinition GetMethod(this TypeDefinition self,
293293
string methodName,
294294
params TypeReference[] paramTypes)
295295
{
296-
return
296+
return GetMethod(self, methodName, paramTypes.Select(p => p.FullName).ToArray());
297+
/*
297298
self.Methods.FirstOrDefault(
298299
m =>
299300
m.Name == methodName
300301
&& paramTypes.SequenceEqual(m.Parameters.Select(p => p.ParameterType), new TypeComparer()));
302+
*/
301303
}
302304

303305
/// <summary>
@@ -313,13 +315,7 @@ public static MethodDefinition GetMethod(this TypeDefinition self,
313315
/// </returns>
314316
public static MethodDefinition GetMethod(this TypeDefinition self, string methodName, params Type[] types)
315317
{
316-
TypeReference[] trs = new TypeReference[types.Length];
317-
for (int i = 0; i < types.Length; i++)
318-
{
319-
trs[i] = ParamHelper.FromType(types[i]);
320-
}
321-
322-
return GetMethod(self, methodName, trs);
318+
return GetMethod(self, methodName, types.Select(t => ParamHelper.FromType(t).FullName).ToArray());
323319
}
324320

325321
/// <summary>
@@ -357,6 +353,58 @@ public static MethodDefinition[] GetMethods(this TypeDefinition self, string met
357353
{
358354
return self.Methods.Where(m => m.Name == methodName).ToArray();
359355
}
356+
357+
/// <summary>
358+
/// Finds the methods with the given name that have at least the provided parameters. The number of parameters need not
359+
/// match, which is why
360+
/// the methods returned may have more parameters than passed to this method.
361+
/// </summary>
362+
/// <param name="self">Reference to type definition that owns the method/member.</param>
363+
/// <param name="methodName">Name of the method to match.</param>
364+
/// <param name="types">Parameter types in the order they should be declared in the method.</param>
365+
/// <returns>An array of methods that have the specified name and *at least* the given parameters.</returns>
366+
public static MethodDefinition[] MatchMethod(this TypeDefinition self, string methodName, params Type[] types)
367+
{
368+
return MatchMethod(self, methodName, types.Select(t => ParamHelper.FromType(t).FullName).ToArray());
369+
}
370+
371+
/// <summary>
372+
/// Finds the methods with the given name that have at least the provided parameters. The number of parameters need not
373+
/// match, which is why
374+
/// the methods returned may have more parameters than passed to this method.
375+
/// </summary>
376+
/// <param name="self">Reference to type definition that owns the method/member.</param>
377+
/// <param name="methodName">Name of the method to match.</param>
378+
/// <param name="paramTypes">Parameter types in the order they should be declared in the method.</param>
379+
/// <returns>An array of methods that have the specified name and *at least* the given parameters.</returns>
380+
public static MethodDefinition[] MatchMethod(this TypeDefinition self,
381+
string methodName,
382+
params TypeReference[] paramTypes)
383+
{
384+
return MatchMethod(self, methodName, paramTypes.Select(p => p.FullName).ToArray());
385+
}
386+
387+
/// <summary>
388+
/// Finds the methods with the given name that have at least the provided parameters. The number of parameters need not
389+
/// match, which is why
390+
/// the methods returned may have more parameters than passed to this method.
391+
/// </summary>
392+
/// <param name="self">Reference to type definition that owns the method/member.</param>
393+
/// <param name="methodName">Name of the method to match.</param>
394+
/// <param name="paramTypes">Parameter types in the order they should be declared in the method.</param>
395+
/// <returns>An array of methods that have the specified name and *at least* the given parameters.</returns>
396+
public static MethodDefinition[] MatchMethod(this TypeDefinition self,
397+
string methodName,
398+
params string[] paramTypes)
399+
{
400+
return
401+
self.Methods.Where(
402+
m =>
403+
m.Name == methodName && paramTypes.Length <= m.Parameters.Count
404+
&& paramTypes.SequenceEqual(
405+
m.Parameters.Take(paramTypes.Length).Select(p => p.ParameterType.FullName),
406+
StringComparer.InvariantCulture)).ToArray();
407+
}
360408
}
361409

362410
internal class TypeComparer : IEqualityComparer<TypeReference>

Tests/TypeDefinitionTests.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public static bool HookTest1(int tag, TypeDefinitionTests test, out byte ret, re
1717
return true;
1818
}
1919

20+
public static bool HookTest2(int tag, TypeDefinitionTests test, out int ret, int val1, byte val2, List<int> val3)
21+
{
22+
ret = 1;
23+
return false;
24+
}
25+
2026
public static void Main(string[] args)
2127
{
2228
AssemblyDefinition ad = AssemblyLoader.LoadAssembly("Tests.exe");
@@ -57,7 +63,7 @@ public static void Main(string[] args)
5763
testType.GetField("testMember"));
5864

5965
InjectionDefinition hd3 = testType.GetInjectionMethod("", null, InjectFlags.None, null, null);
60-
66+
/*
6167
testType.GetMethod("Test2")
6268
.InjectWith(
6369
testType.GetMethod("HookTest1"),
@@ -67,6 +73,12 @@ public static void Main(string[] args)
6773
InjectDirection.Before,
6874
new[] {0},
6975
new[] {testType.GetField("testMember")});
76+
*/
77+
InjectionDefinition hd4 = new InjectionDefinition(testType.GetMethod(nameof(TestPartialParams)), testType.GetMethod(nameof(HookTest2)), (new InjectValues { PassTag = true, PassInvokingInstance = true, ModifyReturn = true, ParameterType = InjectValues.PassParametersType.ByValue}).GetCombinedFlags());
78+
79+
hd4.Inject();
80+
81+
MethodDefinition[] matches = testType.MatchMethod("TestMatch", typeof(int), typeof(string));
7082

7183
Console.WriteLine($"Another hookdef: {hd2}");
7284

@@ -90,8 +102,28 @@ public byte Test2(int val)
90102
return 0;
91103
}
92104

105+
private int TestPartialParams(int val1, byte val2, List<int> val3)
106+
{
107+
return -1;
108+
}
109+
93110
public static void Test3(params object[] asd) {}
94111

112+
protected void TestMatch(int val1, bool val2)
113+
{
114+
Console.WriteLine("I am Groot!");
115+
}
116+
117+
protected void TestMatch(int val1, bool val2, byte val3)
118+
{
119+
Console.WriteLine("I am Groot!");
120+
}
121+
122+
protected void TestMatch(int val1, string val2)
123+
{
124+
Console.WriteLine("I am Groot!");
125+
}
126+
95127
private class Nested1
96128
{
97129
private class Nested2

0 commit comments

Comments
 (0)