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

Request: CodeInstruction generator #42

Closed
FrodoOf9Fingers opened this issue Sep 22, 2017 · 5 comments
Closed

Request: CodeInstruction generator #42

FrodoOf9Fingers opened this issue Sep 22, 2017 · 5 comments

Comments

@FrodoOf9Fingers
Copy link

It would be really nice to have a function that took a Method and returned a list/enumberable of CodeInstructions for use in Transpilers.

@pardeike
Copy link
Owner

Part of the api I want to make public in combination with reverse patching

@FrodoOf9Fingers
Copy link
Author

Awesome, thanks!

@BarryBadpak
Copy link

Any update on this? Would be amazing to have the CodeInstructions generator available in order to replace methods

@pardeike
Copy link
Owner

Just checked it in. The tricky part is that CodeInstructions can have local var references. And those come from registering new variables with the current ILGenerator. So I made this API:

using Harmony;
using Harmony.ILCopying;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace HarmonyConsoleApp
{
	class Program
	{
		static void Main(string[] args)
		{
			var harmony = HarmonyInstance.Create("com.company.project.product");
			harmony.PatchAll(typeof(Program).Assembly);

			var test = new Test();
			test.Call();
			Console.ReadKey();
		}
	}

	public class Test
	{
		public void Extra()
		{
			Console.WriteLine("EXTRA");
		}

		public void Call()
		{
			Console.WriteLine("ORIGINAL");
		}
	}

	[HarmonyPatch(typeof(Test))]
	[HarmonyPatch("Call")]
	static class Patch
	{
		static IEnumerable<CodeInstruction> Transpiler(ILGenerator generator, MethodBase method, IEnumerable<CodeInstruction> instructions)
		{
			var ils1 = MethodBodyReader.GetInstructions(generator, method);
			foreach (var il in ils1)
			{
				var instruction = il.GetCodeInstruction();
				if (instruction.operand as string == "ORIGINAL")
					instruction.operand = "PATCHED";
				if (instruction.opcode != OpCodes.Ret)
					yield return instruction;
			}

			var ils2 = MethodBodyReader.GetInstructions(generator, AccessTools.Method(typeof(Test), "Extra"));
			foreach (var il in ils2)
			{
				var instruction = il.GetCodeInstruction();
				if (instruction.opcode != OpCodes.Ret)
					yield return instruction;
			}

			yield return new CodeInstruction(OpCodes.Ret);
		}
	}
}

Note that in order to get the instructions, you will as a side effect generate local vars on your current generator.

@pardeike
Copy link
Owner

I am going to make a high level reverse patch operation where you can project the ill codes onto an empty static method you have in your code for easier calling inside your patch functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants