0

I want to add method body without going through Emit statements. Similar to:

public static void Replicate<T>(this MethodBuilder methodBuilder, Func<T> func)
{
    var body = func.Method.GetMethodBody();
    var instructions = body.GetILAsByteArray();
    methodBuilder.CreateMethodBody(instructions, instructions.Length);
}
var methodBuilder = typeBuilder.DefineMethod("GetExtensionCollection", MethodAttributes.Public, typeof(Stream), Type.EmptyTypes);
methodBuilder.Replicate(() =>
{
    var assembly = Assembly.GetExecutingAssembly();
    return assembly.GetManifestResourceStream(assembly.GetName().Name + ".Extensions.xml");
});

When I decompile the generated assembly using dnSpy, the IL code is completely wrong, and the C# code throws exception. enter image description here enter image description here

I have also tried the following, based on Create a copy of method from IL and I have modified it to use Mono.Reflection:

public static void Replicate<T>(this MethodBuilder methodBuilder, Func<T> func)
{
    var il = methodBuilder.GetILGenerator();
    foreach (var local in func.Method.GetMethodBody().LocalVariables)
        il.DeclareLocal(local.LocalType);
    foreach (var instrcustion in func.Method.GetInstructions())
    {
        if (instrcustion.OpCode.OperandType == OperandType.InlineBrTarget)
        {
            il.Emit(instrcustion.OpCode, Convert.ToInt32(instrcustion.Operand));
            continue;
        }
        if (instrcustion.OpCode.OperandType == OperandType.ShortInlineBrTarget)
        {
            continue;
        }
        if (instrcustion.OpCode.OperandType == OperandType.InlineString)
        {
            il.Emit(instrcustion.OpCode, instrcustion.Operand.ToString());
            continue;
        }
        if (instrcustion.OpCode.OperandType == OperandType.InlineType)
        {
            il.Emit(instrcustion.OpCode, instrcustion.Operand as Type);
            continue;
        }
        if (instrcustion.OpCode.FlowControl == FlowControl.Call)
        {
            var methodInfo = instrcustion.Operand as MethodInfo;
            if (methodInfo == func.Method)
                il.Emit(instrcustion.OpCode, methodBuilder);
            else
                il.Emit(instrcustion.OpCode, methodInfo);
            continue;
        }
        il.Emit(instrcustion.OpCode);
    }
}

This one works, but I need:

  • Preferably get the first Replicate to work
  • If not successful, build a comprehensive version of the second Replicate
Ehsan Keshavarzian
  • 1,138
  • 1
  • 9
  • 18
  • 4
    Many IL instructions include a metadata token as an argument, these tokens are module specific; so if the method you are copying is defined in a different module, then you will likely need to parse the IL and translate the metadata tokens. Also, the locals are defined in a signature blob rather than IL, so you would also need a way to copy/translate that to. Solving these issues would probably result in something that looks like your second implementation anyway. – Brian Reichle Mar 03 '20 at 08:34
  • @BrianReichle Do you know how I can copy the signature blob? I couldn't replicate it directly, so I tried mixing CreateMethodBody with ILGenerator. Not sure if that's a good idea or not. It didn't work anyways. – Ehsan Keshavarzian Mar 03 '20 at 21:52
  • 2
    [SetMethodBody](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.methodbuilder.setmethodbody) lets you specify the bytes for the locals signature blob. [MethodBody.LocalSignatureMetadata](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodbody.localsignaturemetadatatoken) will provide you with a metadata token you can pass to [Module.ResolveSignature](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.module.resolvesignature) in order to get the locals signature bytes. But translation issues will still apply. – Brian Reichle Mar 03 '20 at 22:12

0 Answers0