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.

Reply via email to