ltrzesniewski / InlineIL.Fody

Inject arbitrary IL code at compile time.
MIT License
240 stars 17 forks source link

Using Calli() causes a build error in Debug configuration #14

Closed akerfoot closed 5 years ago

akerfoot commented 5 years ago

Writing InlineIL to call a function pointer with Calli will trigger a build error. This only happens in the Debug configuration. Release builds and runs as expected.

For example, this code:

public int AddOne(int x)
{
    Ldarg_S(nameof(x));
    Ldarg_0();
    Ldfld(new FieldRef(typeof(Binding), "addOne"));
    var sig = new StandAloneMethodSig(CallingConvention.Cdecl, typeof(int), new TypeRef[] { typeof(int) });
    Calli(sig);
    Ret();

    return 0;
}

causes this:

2>C:\dev\snippets\InlineILTest\src\Test\Binding.cs(39,13,39,24): error : Fody/InlineIL: Unexpected instruction, expected a method call but was: IL_005f: ldloc V_0 (in System.Int32 InlineILTest.Binding::AddOne(System.Int32) at instruction IL_005f: ldloc V_0)
2>MSBUILD : error : Fody: An unhandled exception occurred:
2>MSBUILD : error : Exception:
2>MSBUILD : error : Value does not fall within the expected range.
2>MSBUILD : error : Type:
2>MSBUILD : error : System.ArgumentException
2>MSBUILD : error : StackTrace:
2>MSBUILD : error :    at Mono.Cecil.Pdb.ISymUnmanagedWriter2.DefineLocalVariable2(String name, Int32 attributes, Int32 sigToken, Int32 addrKind, Int32 addr1, Int32 addr2, Int32 addr3, Int32 startOffset, Int32 endOffset)
2>MSBUILD : error :    at Mono.Cecil.Pdb.NativePdbWriter.DefineLocalVariable(VariableDebugInformation variable, Int32 local_var_token, Int32 start_offset, Int32 end_offset) in C:\Code\Fody\cecil\symbols\pdb\Mono.Cecil.Pdb\NativePdbWriter.cs:line 215
2>MSBUILD : error :    at Mono.Cecil.Pdb.NativePdbWriter.DefineScope(ScopeDebugInformation scope, MethodDebugInformation info, MetadataToken& import_parent) in C:\Code\Fody\cecil\symbols\pdb\Mono.Cecil.Pdb\NativePdbWriter.cs:line 177
2>MSBUILD : error :    at Mono.Cecil.Pdb.NativePdbWriter.Write(MethodDebugInformation info) in C:\Code\Fody\cecil\symbols\pdb\Mono.Cecil.Pdb\NativePdbWriter.cs:line 69
2>MSBUILD : error :    at Mono.Cecil.Cil.CodeWriter.WriteResolvedMethodBody(MethodDefinition method) in C:\Code\Fody\cecil\Mono.Cecil.Cil\CodeWriter.cs:line 134
2>MSBUILD : error :    at Mono.Cecil.Cil.CodeWriter.WriteMethodBody(MethodDefinition method) in C:\Code\Fody\cecil\Mono.Cecil.Cil\CodeWriter.cs:line 54
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1658
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1650
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1440
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddTypes() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1412
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.BuildTypes() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1266
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.BuildModule() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1036
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.BuildMetadata() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1006
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.<>c.<BuildMetadata>b__2_0(MetadataBuilder builder, MetadataReader _) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 144
2>MSBUILD : error :    at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read) in C:\Code\Fody\cecil\Mono.Cecil\ModuleDefinition.cs:line 947
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.BuildMetadata(ModuleDefinition module, MetadataBuilder metadata) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 143
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 119
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 78
2>MSBUILD : error :    at Mono.Cecil.ModuleDefinition.Write(String fileName, WriterParameters parameters) in C:\Code\Fody\cecil\Mono.Cecil\ModuleDefinition.cs:line 1136
2>MSBUILD : error :    at InnerWeaver.WriteModule() in C:\projects\fody\FodyIsolated\ModuleWriter.cs:line 19
2>MSBUILD : error :    at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 111
2>MSBUILD : error : Source:
2>MSBUILD : error : Mono.Cecil.Pdb
2>MSBUILD : error : TargetSite:
2>MSBUILD : error : Void DefineLocalVariable2(System.String, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32)
akerfoot commented 5 years ago

Here is a sample project that demonstrates the error InlineILTest.tar.gz

I noticed a similar-looking error with a different weaver reported here: https://github.com/Fody/Fody/issues/312

akerfoot commented 5 years ago

The above error occurs when Debugging Information is set to Full.

When generating a Portable PDB, or none at all, I get a different error:

MSBUILD : error : Fody/InlineIL: Unexpected instruction, expected a method call but was: IL_005f: ldloc V_0 (in System.Int32 InlineILTest.Binding::AddOne(System.Int32) at instruction IL_005f: ldloc V_0)

Again, it works fine in Release.

ltrzesniewski commented 5 years ago

The "Unexpected instruction" error is correct here: InlineIL expects your code to pass "constant" (for lack of a better word) compile-time arguments to the IL-emitting methods, but you pass a local variable to the Calli method instead.

You need to write that code like this:

Calli(new StandAloneMethodSig(...));

Your code works in Release mode because the compiler sees the local variable is not needed, and eliminates it. Debug builds produce code which is closer to the source C# in this regard.

I probably should improve the error message and document this better.

What is not expected is the "Fody: An unhandled exception occurred:" part though. The PDB should have been written correctly (or maybe Fody shouldn't output the assembly at all if there were errors... hmm). Portable PDB works as expected. I'll take a look at why the legacy PDB writer fails. Thanks for the repro!

ltrzesniewski commented 5 years ago

Oh, also you should replace this:

Ret();
return 0;

With this:

return IL.Return<int>();

As the former code will leave an unreachable IL block (the return 0; part).

ltrzesniewski commented 5 years ago

The "An unhandled exception occurred" part should be fixed in v1.3.1. I also added a clarification to the error message and to the readme.

akerfoot commented 5 years ago

Perfect, thank you!