public virtual bool SearchMethod(MethodInfo searchMethod, List <MethodInfo> targets, out List <ItemPos <MethodInfo> > Results) { Results = new List <ItemPos <MethodInfo> >(); List <CodeInstruction> instructionList; try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); } catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); } bool foundResult = false; for (int i = 0; i < instructionList.Count; i++) { CodeInstruction instruction = instructionList[i]; if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo calledMethod && targets.Exists(t => IsBaseMethod(t, calledMethod))) { foundResult = true; Results.Add((ItemPos <MethodInfo>)(i, calledMethod)); if (!FindAll) { return(true); } } } return(foundResult); }
public virtual bool SearchMethod(MethodInfo searchMethod, out List <ItemPos <JobDef> > Results) { Results = new List <ItemPos <JobDef> >(); bool foundResult = false; List <CodeInstruction> instructionList; try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); } catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); } for (int i = 0; i < instructionList.Count; i++) { CodeInstruction instruction = instructionList[i]; // Ambiguous method call if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo calledMethod) { if (calledMethod.ReturnType == typeof(JobDef)) { foundResult = true; Results.Add((ItemPos <JobDef>)(i, null)); } } // StatDefOf call if (instruction.opcode == OpCodes.Ldsfld && instruction.operand is FieldInfo jobField && jobField.FieldType == typeof(JobDef) && JobDefOfFieldInfo.Contains(jobField)) { Results.Add((ItemPos <JobDef>)(i, FieldToJob[jobField])); foundResult = true; } if (!FindAll && foundResult) { return(foundResult); } } return(foundResult); }
private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions) { var inst = PatchProcessor.GetOriginalInstructions(__originalMethod); var modInstList = instructions.ToList(); var insts = new Myers <CodeInstruction>(inst.ToArray(), modInstList.ToArray(), methComparer); insts.Compute(); var key = Utility.GetMethodKey(__originalMethod as MethodInfo); var index = MethodInfoCache.AddMethod(key, __originalMethod as MethodInfo); foreach (var thing in insts.changeSet) { // We only want added methods if (thing.change != ChangeType.Added) { continue; } if (!InternalMethodUtility.IsFunctionCall(thing.value.opcode) || !(thing.value.operand is MethodInfo meth)) { continue; } // swap our instruction var replaceInstruction = MethodTransplanting.ReplaceMethodInstruction( thing.value, key, typeof(H_HarmonyTranspilersInternalMethods), index); // Find the place it was in our method, and replace the instruction (Optimisation Opportunity to improve this) for (var i = 0; i < modInstList.Count; i++) { var instruction = modInstList[i]; if (!InternalMethodUtility.IsFunctionCall(instruction.opcode)) { continue; } if (!(instruction.operand is MethodInfo info) || info.Name != meth.Name) { continue; } if (instruction != replaceInstruction) { modInstList[i] = replaceInstruction; } break; } } return(modInstList); }
/* The idea here is a little convoluted, but in short * 1. Grab the 'original' instructions of the method, and grab all the 'Call'/'CallVirt' instructions * 2. Grab all the 'Call'/'CallVirt' instructions in the modified version */ public static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions, ILGenerator ilGen) { var inst = PatchProcessor.GetOriginalInstructions(__originalMethod); var modInstList = instructions.ToList(); var origCalls = inst.Where(i => InternalMethodUtility.IsFunctionCall(i.opcode)).ToList(); var modCalls = modInstList.Where(i => InternalMethodUtility.IsFunctionCall(i.opcode)).ToList(); return(instructions); }
public void Test_Read_WeirdMethodWithGoto() { var method = SymbolExtensions.GetMethodInfo(() => WeirdMethodWithGoto()); Assert.NotNull(method); var instructions = PatchProcessor.GetOriginalInstructions(method); Assert.NotNull(instructions); Assert.Greater(instructions.Count, 0); }
internal static MethodBase TargetMethod() { var shwGetter = ReflectionHelper.Settings.Type.GetProperty("ShowHaxchiWarning").GetGetMethod(); return((from method in ReflectionHelper.NusGrabberForm.Type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) where method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType.IsAbstract let instructions = PatchProcessor.GetOriginalInstructions(method, out _) where instructions.Any(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand) == shwGetter) select method).FirstOrDefault()); }
static string GetMethodHash(MethodBase method) { var toHash = new List <byte>(); foreach (var ins in PatchProcessor.GetOriginalInstructions(method)) { toHash.Add(Encoding.UTF8.GetBytes(ins.opcode.Name + ins.operand)); } using (var sha256 = System.Security.Cryptography.SHA256.Create()) { return(Convert.ToBase64String(sha256.ComputeHash(toHash.ToArray()))); } }
public static CodeInstructionDifference CodeInstructionDifferencesBetweenOriginalAndTranspiledMethod( IPatch patch, MethodInfo patchedMethod, MethodInfo transpilerMethod) { List <CodeInstruction> originalInstructions = PatchProcessor.GetOriginalInstructions(patchedMethod); List <CodeInstruction> codeInstructionList; patch.Apply(); codeInstructionList = (List <CodeInstruction>)transpilerMethod.Invoke(null, new object[] { originalInstructions }); return(new CodeInstructionDifference(originalInstructions, codeInstructionList)); }
private static FieldInfo GetFieldInfo(MethodInfo getterMethod) { List <CodeInstruction> instructionList; try { instructionList = PatchProcessor.GetCurrentInstructions(getterMethod); } catch { instructionList = PatchProcessor.GetOriginalInstructions(getterMethod); } var length = instructionList.Count; for (int i = 0; i < length; i++) { CodeInstruction instruction = instructionList[length - 1 - i]; if (instruction.opcode == OpCodes.Ldfld && instructionList[length - 2 - i].IsLdarg(0)) { return(instruction.operand as FieldInfo); } } return(null); }
// Based on https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs // and https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs public static int GetMethodDebugId(this MethodBase method) { string cur = null; try { // Try extract the debug id from the method body foreach (var inst in PatchProcessor.GetOriginalInstructions(method)) { // Example class names: <>c__DisplayClass10_0 or <CompGetGizmosExtra>d__7 if (inst.opcode == OpCodes.Newobj && inst.operand is MethodBase m && (cur = m.DeclaringType.Name) != null) { if (cur.StartsWith(DisplayClassPrefix)) { return(int.Parse(cur.Substring(DisplayClassPrefix.Length).Until('_'))); } else if (cur.Contains(EnumerableStateMachineInfix)) { return(int.Parse(cur.After('>').Substring(EnumerableStateMachineInfix.Length))); } } // Example method names: <FillTab>b__10_0 or <DoWindowContents>g__Start|55_1 else if ( (inst.opcode == OpCodes.Ldftn || inst.opcode == OpCodes.Call) && inst.operand is MethodBase f && (cur = f.Name) != null && cur.StartsWith("<") && cur.After('>').CharacterCount('_') == 3) { if (cur.Contains(LambdaMethodInfix)) { return(int.Parse(cur.After('>').Substring(LambdaMethodInfix.Length).Until('_'))); } else if (cur.Contains(LocalFunctionInfix)) { return(int.Parse(cur.After('|').Until('_'))); } } }
private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions) { var inst = PatchProcessor.GetOriginalInstructions(__originalMethod); var modInstList = instructions.ToList(); var insts = new Myers <CodeInstruction>(inst.ToArray(), modInstList.ToArray(), methComparer); insts.Compute(); var key = Utility.GetMethodKey(__originalMethod); var index = MethodInfoCache.AddMethod(key, __originalMethod); foreach (var thing in insts.changeSet) { // We only want added methods if (thing.change != ChangeType.Added) { continue; } if (!Utility.ValidCallInstruction(thing.value, null, out var meth, out _)) { continue; } if (!(meth is MethodInfo)) { continue; } // swap our instruction var replaceInstruction = MethodTransplanting.ReplaceMethodInstruction( thing.value, key, typeof(H_HarmonyTranspilersInternalMethods), index); modInstList[thing.rIndex] = replaceInstruction; } return(modInstList); }
private bool SearchToilGenerator(MethodInfo searchMethod, List <MethodInfo> ActionList, out List <MethodInfo> ActionsFound) { ActionsFound = new List <MethodInfo>(); bool foundResult = false; List <CodeInstruction> instructionList; try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); } catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); } foreach (var instruction in instructionList) { if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Ldftn) && instruction.operand is MethodInfo calledMethod && ActionList.Any(t => IsBaseMethod(t, calledMethod))) { foundResult = true; ActionsFound.Add(calledMethod); if (!FindAll) { return(true); } } } return(foundResult); }
public virtual bool SearchMethod(MethodInfo searchMethod, out List <ItemPos <StatDef> > Results, out List <CodeInstruction> PawnIns) { Results = new List <ItemPos <StatDef> >(); PawnIns = new List <CodeInstruction>(); bool foundResult = false; bool foundPawn = false; List <CodeInstruction> instructionList; try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); } catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); } List <int?> ParamList = searchMethod.GetParameters()?.Where(t => t.ParameterType.IsAssignableFrom(typeof(Pawn)))?.Select(t => new int?(t.Position))?.ToList() ?? new List <int?>(); List <LocalBuilder> LocalList = new List <LocalBuilder>(); if (!ParamList.NullOrEmpty()) { foundPawn = true; } for (int i = 0; i < instructionList.Count; i++) { CodeInstruction instruction = instructionList[i]; if (instruction.opcode == OpCodes.Ldfld && instruction.operand is FieldInfo pawnField && pawnField.FieldType.IsAssignableFrom(typeof(Pawn))) { foundPawn = true; for (int j = 1; j < i; j++) { if (instructionList[i - j].IsLdarg(0)) { PawnIns = new List <CodeInstruction>(instructionList.GetRange(i - j, j + 1)); break; } } } if (ParamList.Any(t => instruction.IsLdarg(t))) { PawnIns = new List <CodeInstruction>() { instruction } } ; if (instruction.IsStloc() && instruction.operand is LocalBuilder local && !LocalList.Contains(local) && local.LocalType.IsAssignableFrom(typeof(Pawn))) { LocalList.Add(local); } if (LocalList.Any(t => instruction.IsLdloc(t))) { PawnIns = new List <CodeInstruction>() { instruction } } ; // Ambiguous method call if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo calledMethod) { if (calledMethod.ReturnType == typeof(StatDef)) { foundResult = true; Results.Add((ItemPos <StatDef>)(i, null)); } else if (calledMethod.ReturnType.IsAssignableFrom(typeof(Pawn))) { foundPawn = true; if (calledMethod.IsStatic) { PawnIns = new List <CodeInstruction>() { instruction } } ; else { for (int j = 1; j < i; j++) { if (instructionList[i - j].IsLdarg(0)) { PawnIns = new List <CodeInstruction>(instructionList.GetRange(i - j, j + 1)); break; } } } } } // StatDefOf call if (instruction.opcode == OpCodes.Ldsfld && instruction.operand is FieldInfo statField && statField.FieldType == typeof(StatDef) && StatDefOfFieldInfo.Contains(statField)) { Results.Add((ItemPos <StatDef>)(i, FieldToStat[statField])); foundResult = true; } if (!FindAll && foundResult && (!findPawn || foundPawn)) { return(foundResult); } } return(foundResult); } } }