private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> codeInstructions) { var enumerable = codeInstructions.ToList(); try { List <CodeInstruction> instructions = enumerable; for (int i = 0; i < instructions.Count; i++) { if (!IsFunctionCall(instructions[i].opcode)) { continue; } if (!(instructions[i].operand is MethodInfo meth)) { continue; } // Check for constrained if (i != 0 && instructions[i - 1].opcode == OpCodes.Constrained) { continue; } // Make sure it is not an analyzer profiling method if (meth.DeclaringType.FullName.Contains("Analyzer.Profiling")) { continue; } var key = Utility.GetMethodKey(meth); var index = MethodInfoCache.AddMethod(key, meth); var inst = MethodTransplanting.ReplaceMethodInstruction( instructions[i], key, GUIController.types[__originalMethod.DeclaringType + ":" + __originalMethod.Name + "-int"], index); instructions[i] = inst; } return(instructions); } catch (Exception e) { #if DEBUG ThreadSafeLogger.Error($"Failed to patch the internal method {__originalMethod.DeclaringType.FullName}:{__originalMethod.Name}, failed with the error " + e.Message); #else if (Settings.verboseLogging) { ThreadSafeLogger.Warning($"Failed to patch the internal method {__originalMethod.DeclaringType.FullName}:{__originalMethod.Name}, failed with the error " + e.Message); } #endif return(enumerable); } }
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); }
public static void ClearPatchCaches() { patchedAssemblies.Clear(); patchedTypes.Clear(); patchedMethods.Clear(); InternalMethodUtility.ClearCaches(); MethodTransplanting.ClearCaches(); TranspilerMethodUtility.ClearCaches(); MethodInfoCache.ClearCache(); #if DEBUG ThreadSafeLogger.Message("[Analyzer] Cleared all caches"); #endif }
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 static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> codeInstructions) { try { var instructions = codeInstructions.ToList(); for (int i = 0; i < instructions.Count; i++) { if (!Utility.ValidCallInstruction(instructions[i], i == 0 ? null : instructions[i - 1], out var meth, out var key)) { continue; } var index = MethodInfoCache.AddMethod(key, meth); var inst = MethodTransplanting.ReplaceMethodInstruction( instructions[i], key, GUIController.types[__originalMethod.DeclaringType + ":" + __originalMethod.Name + "-int"], index); instructions[i] = inst; } return(instructions); } catch (Exception e) { #if DEBUG ThreadSafeLogger.ReportException(e, $"Failed to patch the methods inside {Utility.GetSignature(__originalMethod, false)}"); #else if (Settings.verboseLogging) { ThreadSafeLogger.ReportException(e, $"Failed to patch the methods inside {Utility.GetSignature(__originalMethod, false)}"); } #endif return(codeInstructions); } }
// This transpiler basically replicates ProfileController.Start, but in IL, and inside the method it is patching, to reduce as much overhead as // possible, its quite simple, just long and hard to read. public static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions, ILGenerator ilGen) { var profLocal = ilGen.DeclareLocal(typeof(Profiler)); var keyLocal = ilGen.DeclareLocal(typeof(string)); var beginLabel = ilGen.DefineLabel(); var noProfLabel = ilGen.DefineLabel(); var curType = typeInfo[__originalMethod]; var curLabelMeth = curType.GetMethod("GetLabel", BindingFlags.Public | BindingFlags.Static); var curNamerMeth = curType.GetMethod("GetName", BindingFlags.Public | BindingFlags.Static); var curTypeMeth = curType.GetMethod("GetType", BindingFlags.Public | BindingFlags.Static); var key = Utility.GetMethodKey(__originalMethod as MethodInfo); // This translates our method into a human-legible key, I.e. Namespace.Type<Generic>:Method var methodKey = MethodInfoCache.AddMethod(key, __originalMethod as MethodInfo); // Active Check { // if(active && (Analyzer.CurrentlyProfiling)) yield return(new CodeInstruction(OpCodes.Ldsfld, curType.GetField("Active", BindingFlags.Public | BindingFlags.Static))); yield return(new CodeInstruction(OpCodes.Ldsfld, Analyzer_CurrentlyProfiling)); // profiling yield return(new CodeInstruction(OpCodes.Ldsfld, Analyzer_CurrentyPaused)); // !paused yield return(new CodeInstruction(OpCodes.Not)); yield return(new CodeInstruction(OpCodes.And)); yield return(new CodeInstruction(OpCodes.And)); yield return(new CodeInstruction(OpCodes.Brfalse_S, beginLabel)); } { // Custom Namer if (curNamerMeth != null) { foreach (var codeInst in GetLoadArgsForMethodParams(__originalMethod as MethodInfo, curNamerMeth.GetParameters())) { yield return(codeInst); } yield return(new CodeInstruction(OpCodes.Call, curNamerMeth)); } else { yield return(new CodeInstruction(OpCodes.Ldstr, key)); } yield return(new CodeInstruction(OpCodes.Stloc, keyLocal)); } { // if(Profilers.TryGetValue(key, out var prof)) yield return(new CodeInstruction(OpCodes.Ldsfld, ProfilerController_Profiles)); yield return(new CodeInstruction(OpCodes.Ldloc, keyLocal)); yield return(new CodeInstruction(OpCodes.Ldloca_S, profLocal)); yield return(new CodeInstruction(OpCodes.Callvirt, Dict_TryGetValue)); yield return(new CodeInstruction(OpCodes.Brfalse_S, noProfLabel)); } { // If we found a profiler - Start it, and skip to the start of execution of the method yield return(new CodeInstruction(OpCodes.Ldloc, profLocal)); yield return(new CodeInstruction(OpCodes.Call, Profiler_Start)); yield return(new CodeInstruction(OpCodes.Pop)); // Profiler.Start returns itself so we pop it off the stack yield return(new CodeInstruction(OpCodes.Br, beginLabel)); } { // if not, we need to make one yield return(new CodeInstruction(OpCodes.Ldloc, keyLocal).WithLabels(noProfLabel)); { // Custom Labelling if (curLabelMeth != null) { foreach (var codeInst in GetLoadArgsForMethodParams(__originalMethod as MethodInfo, curLabelMeth.GetParameters())) { yield return(codeInst); } yield return(new CodeInstruction(OpCodes.Call, curLabelMeth)); } else { yield return(new CodeInstruction(OpCodes.Dup)); // duplicate the key on the stack so the key is both the key and the label in ProfileController.Start } } { // Custom Typing if (curTypeMeth != null) { foreach (var codeInst in GetLoadArgsForMethodParams(__originalMethod as MethodInfo, curTypeMeth.GetParameters())) { yield return(codeInst); } yield return(new CodeInstruction(OpCodes.Call, curTypeMeth)); } else { yield return(new CodeInstruction(OpCodes.Ldnull)); // duplicate the key on the stack so the key is both the key and the label in ProfileController.Start } } yield return(new CodeInstruction(OpCodes.Ldnull)); yield return(new CodeInstruction(OpCodes.Ldnull)); { // get our methodinfo from the metadata foreach (var inst in MethodInfoCache.GetInlineIL(methodKey)) { yield return(inst); } } yield return(new CodeInstruction(OpCodes.Newobj, ProfilerCtor)); // new Profiler(); yield return(new CodeInstruction(OpCodes.Dup)); yield return(new CodeInstruction(OpCodes.Stloc, profLocal)); } yield return(new CodeInstruction(OpCodes.Call, Profiler_Start)); yield return(new CodeInstruction(OpCodes.Pop)); // profiler.Start returns itself { // Add to the Profilers dictionary, so we cache creation. yield return(new CodeInstruction(OpCodes.Ldsfld, ProfilerController_Profiles)); yield return(new CodeInstruction(OpCodes.Ldloc, keyLocal)); yield return(new CodeInstruction(OpCodes.Ldloc, profLocal)); yield return(new CodeInstruction(OpCodes.Callvirt, Dict_Add)); } instructions.ElementAt(0).WithLabels(beginLabel); // For each instruction which exits this function, append our finishing touches (I.e.) // if(profiler != null) // { // profiler.Stop(); // } // return; // any labels here are moved to the start of the `if` foreach (var inst in instructions) { if (inst.opcode == OpCodes.Ret) { Label endLabel = ilGen.DefineLabel(); // localProf?.Stop(); yield return(new CodeInstruction(OpCodes.Ldloc, profLocal).MoveLabelsFrom(inst)); yield return(new CodeInstruction(OpCodes.Brfalse_S, endLabel)); yield return(new CodeInstruction(OpCodes.Ldloc, profLocal)); yield return(new CodeInstruction(OpCodes.Call, Profiler_Stop)); yield return(inst.WithLabels(endLabel)); } else { yield return(inst); } } }