public static Action <MethodDef> ApplyInject(this MethodBase inject, PatchConfiguration patch) => stub => { CilBody stubBody = stub.Body; MethodBody injectBody = inject.GetMethodBody(); List <Instruction> originalIL = new List <Instruction>(stubBody.Instructions); List <Local> originalLocals = new List <Local>(stubBody.Variables); stubBody.Variables.Clear(); foreach (LocalVariableInfo local in injectBody.LocalVariables.OrderBy(lvi => lvi.LocalIndex)) { stubBody.Variables.Add(stub.Module.ToDNLib(local)); } List <Instruction> mixinIL = new CilParser(inject.Module, stubBody.Variables, injectBody.GetILAsByteArray()).Parse(); mixinIL.SimplifyMacros(stubBody.Variables, stub.Parameters); IList <Instruction> newIL = stubBody.Instructions; newIL.Clear(); foreach (Instruction inst in mixinIL) { switch (inst.Operand) { case FieldInfo field: inst.Operand = patch.ResolveOrImport(stub.Module, field); break; case MethodBase method: if (method.IsDefined(typeof(BaseDependencyAttribute))) { throw new InvalidOperationException("attempt to inject a body with a base dependency call"); } inst.Operand = patch.ResolveOrImport(stub.Module, method); break; case Type type: inst.Operand = patch.ResolveOrImport(stub.Module, type); break; case byte[] blob: throw new NotImplementedException("how do you import this?"); case MemberInfo member: throw new NotImplementedException("how do you import this?"); } newIL.Add(inst); } stubBody.ExceptionHandlers.Clear(); foreach (ExceptionHandlingClause ehc in injectBody.ExceptionHandlingClauses) { stubBody.ExceptionHandlers.Add(ehc.ToDNLib(stub.Module, newIL)); } stubBody.OptimizeMacros(); };
public static Action <MethodDef> ApplyMixin(this MethodBase mixin, PatchConfiguration patch) => original => { CilBody originalBody = original.Body; MethodBody mixinBody = mixin.GetMethodBody(); List <Instruction> originalIL = new List <Instruction>(originalBody.Instructions); List <Local> originalLocals = new List <Local>(originalBody.Variables); originalBody.Variables.Clear(); foreach (LocalVariableInfo local in mixinBody.LocalVariables.OrderBy(lvi => lvi.LocalIndex)) { originalBody.Variables.Add(original.Module.ToDNLib(local)); } List <Instruction> mixinIL = new CilParser(mixin.Module, originalBody.Variables, mixinBody.GetILAsByteArray()).Parse(); mixinIL.SimplifyMacros(originalBody.Variables, original.Parameters); IList <Instruction> newIL = originalBody.Instructions; newIL.Clear(); int ilStart = 0; if (mixin.IsConstructor) { foreach (Instruction inst in mixin.IsDefined(typeof(RewriteBaseAttribute)) ? mixinIL : originalIL) { ilStart++; newIL.Add(inst); if (inst.OpCode.FlowControl == FlowControl.Call) { break; } } RemoveCall(originalIL); RemoveCall(mixinIL); } MethodDef baseCopy = new MethodDefUser(original.DeclaringType.FindUnusedMethodName(original.Name + "<Base>$"), original.MethodSig, original.ImplAttributes, original.Attributes & CopyMask | CopyAttr); bool useBase = false; foreach (Instruction inst in mixinIL) { switch (inst.Operand) { case FieldInfo field: inst.Operand = patch.ResolveOrImport(original.Module, field); break; case MethodBase method: if (method.IsDefined(typeof(BaseDependencyAttribute))) { useBase = true; inst.Operand = baseCopy; break; } inst.Operand = patch.ResolveOrImport(original.Module, method); break; case Type type: inst.Operand = patch.ResolveOrImport(original.Module, type); break; case byte[] blob: throw new NotImplementedException("how do you import this?"); case MemberInfo member: throw new NotImplementedException("how do you import this?"); } newIL.Add(inst); } if (useBase) { baseCopy.Body = new CilBody(originalBody.InitLocals, originalIL, new List <ExceptionHandler>(originalBody.ExceptionHandlers), originalBody.Variables); original.DeclaringType.Methods.Add(baseCopy); } originalBody.ExceptionHandlers.Clear(); foreach (ExceptionHandlingClause ehc in mixinBody.ExceptionHandlingClauses) { originalBody.ExceptionHandlers.Add(ehc.ToDNLib(original.Module, newIL)); } originalBody.OptimizeMacros(); void RemoveCall(List <Instruction> il) { for (int index = 0; index < il.Count; index++) { if (il[index].OpCode.FlowControl != FlowControl.Call) { continue; } il.RemoveRange(0, index + 1); break; } } };