Hi Charles, Thanks for sharing this!
I haven't proof read all the code, but I don't think there's a much better way than iterating over all the instructions and to recreate the method body and creating appropriate references to operands. One thing that I think is missing is to fixup the jump targets and switches to instructions in the newly created body. It works because you recreate the same body, but from a logical POV, it shouldn't :) Jb On Wed, Jul 1, 2015 at 9:41 PM, Charles Pan <[email protected]> wrote: > We have a domain specific language; we compile it into C# source files and > use the C# compiler to generate the .NET assembly. The generated .NET > assembly is very big, if we only make changes in one method, we don't want > to compile all the methods and generate the big .NET assembly, that will > take too long. The idea is to only compile the modified method, generate a > small .NET assembly that only contains the modified methods, grab the IL > from this small assembly, and update the IL code in the bigger assembly. > This is essentially copying a method from one module to another. > > I've done some searches on this forum on this topic. There are some very > helpful information, but they don't provide enough detail. I am attaching > my version of the CopyMethod() implementation, in the hope that: > > 1. Can anyone come up with a more efficient approach? or is there anything > missing in my code (I left out generic type on purpose). Any input is > appreciated. > 2. You are welcome to improve/re-use the code. > > Here's my code: > > /// <summary> > /// Copy a method from one module to another. If the same method > exists in the target module, the caller > /// is responsible to delete it first. > /// The sourceMethod makes calls to other methods, we divide the > calls into two types: > /// 1. MethodDefinition : these are methods that are defined in the > same module as the sourceMethod; > /// 2. MethodReference : these are methods that are defined in a > different module > /// For type 1 calls, we will copy these MethodDefinitions to the > same target typedef. > /// For type 2 calls, we will not copy the called method > /// > /// Another limitation: any TypeDefinitions that are used in the > sourceMethod will not be copied to the target module; a > /// typereference is created instead. > /// </summary> > /// <param name="copyToTypedef">The typedef to copy the method > to</param> > /// <param name="sourceMethod">The method to copy</param> > /// <returns></returns> > public static MethodDefinition CopyMethod(TypeDefinition > copyToTypedef, MethodDefinition sourceMethod) > { > > ModuleDefinition targetModule = copyToTypedef.Module; > > // create a new MethodDefinition; all the content of > sourceMethod will be copied to this new MethodDefinition > > MethodDefinition targetMethod = new > MethodDefinition(sourceMethod.Name, sourceMethod.Attributes, > targetModule.Import(sourceMethod.ReturnType)); > > > // Copy the parameters; > foreach(ParameterDefinition p in sourceMethod.Parameters) > { > ParameterDefinition nP = new ParameterDefinition(p.Name, > p.Attributes, targetModule.Import(p.ParameterType)); > targetMethod.Parameters.Add(nP); > } > > // copy the body > MethodBody nBody = targetMethod.Body; > MethodBody oldBody = sourceMethod.Body; > > nBody.InitLocals = oldBody.InitLocals; > > // copy the local variable definition > foreach(VariableDefinition v in oldBody.Variables) > { > VariableDefinition nv = new VariableDefinition(v.Name, > targetModule.Import(v.VariableType)); > nBody.Variables.Add(nv); > } > > // copy the IL; we only need to take care of reference and > method definitions > Mono.Collections.Generic.Collection<Instruction> col = > nBody.Instructions; > foreach (Instruction i in oldBody.Instructions) > { > object operand = i.Operand; > if (operand == null) > { > col.Add(new Instruction(i.OpCode, null)); > continue; > } > > // for any methodef that this method calls, we will copy it > > if (operand is MethodDefinition) > { > MethodDefinition dmethod = operand as MethodDefinition; > MethodDefinition newMethod = CopyMethod(copyToTypedef, > dmethod); > col.Add(new Instruction(i.OpCode, newMethod)); > continue; > } > > // for member reference, import it > if (operand is FieldReference) > { > FieldReference fref = operand as FieldReference; > FieldReference newf = targetModule.Import(fref); > col.Add(new Instruction(i.OpCode, newf)); > continue; > } > if (operand is TypeReference) > { > TypeReference tref = operand as TypeReference; > TypeReference newf = targetModule.Import(tref); > col.Add(new Instruction(i.OpCode, newf)); > continue; > } > if (operand is TypeDefinition) > { > TypeDefinition tdef = operand as TypeDefinition; > TypeReference newf = targetModule.Import(tdef); > col.Add(new Instruction(i.OpCode, newf)); > continue; > } > if (operand is MethodReference) > { > MethodReference mref = operand as MethodReference; > MethodReference newf = targetModule.Import(mref); > col.Add(new Instruction(i.OpCode, newf)); > continue; > } > > // we don't need to do any processing on the operand > col.Add(new Instruction(i.OpCode, operand)); > } > > // copy the exception handler blocks > > foreach (ExceptionHandler eh in oldBody.ExceptionHandlers) > { > ExceptionHandler neh = new ExceptionHandler(eh.HandlerType); > neh.CatchType = targetModule.Import(eh.CatchType); > > // we need to setup neh.Start and End; these are > instructions; we need to locate it in the source by index > if (eh.TryStart != null) > { > int idx = oldBody.Instructions.IndexOf(eh.TryStart); > neh.TryStart = col[idx]; > } > if (eh.TryEnd != null) > { > int idx = oldBody.Instructions.IndexOf(eh.TryEnd); > neh.TryEnd = col[idx]; > } > > nBody.ExceptionHandlers.Add(neh); > } > > // Add this method to the target typedef > copyToTypedef.Methods.Add(targetMethod); > targetMethod.DeclaringType = copyToTypedef; > return targetMethod; > } > > -- > -- > -- > mono-cecil > --- > You received this message because you are subscribed to the Google Groups > "mono-cecil" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout. -- -- -- mono-cecil --- You received this message because you are subscribed to the Google Groups "mono-cecil" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
