Skip to content

Can't emit Ldftn with symbolDocumentWriter #6

@odinmillion

Description

@odinmillion

We are trying to emit Ldftn instruction. In some cases we see exception System.ArgumentException.

Look at this code:

public class Dummy
{
  public static int Add10(int x)
  {
    return x + 10;
  }
}
[TestFixture]
public class Tests
{
  [TestCase(true)]
  [TestCase(false)]
  public void TestLdftn(bool useDocumentWriter)
  {
    var id = $"asm-{Guid.NewGuid()}";
    var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(id), AssemblyBuilderAccess.RunAndCollect);
    var module = assembly.DefineDynamicModule(name: id, fileName: id + ".dll", emitSymbolInfo: true);
    var fooTypeBuilder = module.DefineType("Foo", TypeAttributes.Public | TypeAttributes.Class);
    var symWriter = module.GetSymWriter();
    var barMethod = fooTypeBuilder.DefineMethod("Bar", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int) });
    var documentName = fooTypeBuilder.Name + "." + barMethod.Name + ".cil";
    var documentWriter = symWriter.DefineDocument(documentName, SymDocumentType.Text, SymLanguageType.ILAssembly, Guid.Empty);
    using (var il = useDocumentWriter
      ? new GroboIL(barMethod, documentWriter)
      : new GroboIL(barMethod))
    {
      il.Ldnull(); // stack: [null]
      il.Ldftn(typeof(Dummy).GetMethod("Add10")); // stack: [null, Add10*]
      il.Newobj(typeof(Func<int, int>).GetConstructor(new[] { typeof(object), typeof(IntPtr) })); // stack: [func]
      il.Ldc_I4(5); // stack: [func, 5]
      il.Call(typeof(Func<int, int>).GetMethod("Invoke")); // stack: [int]
      il.Ret();
    }
    var fooType = fooTypeBuilder.CreateType();

    var result = fooType.GetMethod("Bar").Invoke(null, new object[] { 5 });

    result.Should().Be(15);
  }
}

We can succesfully emit and run code without document writer. But we see that exception in opposite scenario:

System.ArgumentException : Не удается передать указанный код операции в метод EmitCall.
Имя параметра: opcode
   в System.Reflection.Emit.ILGenerator.EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes)
   в GrEmit.GroboIL.Emit(OpCode opCode, ILInstructionParameter parameter) в C:\BuildAgent\work\249acc80a2fd7042\GrEmit\GrEmit\GroboIL.cs:строка 2000
   в GrEmit.GroboIL.Dispose() в C:\BuildAgent\work\249acc80a2fd7042\GrEmit\GrEmit\GroboIL.cs:строка 147
   в GremitBugs.Tests.TestLdftn(Boolean useDocumentWriter) в C:\Users\****\Documents\Visual Studio 2017\Projects\GremitWeaks\GremitBugs\Tests.cs:строка 45

We assume that bug is located here:

else if(parameter is MethodILInstructionParameter)
il.EmitCall(opCode, ((MethodILInstructionParameter)parameter).Method, null);

Sometimes MethodILInstructionParameter leads to emit non call opcodes :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions