Have I made a mistake in this IL I'm not seeing?

c#, il, reflection.emit

Solution

@leppie got it in the comments: the stack needs to be deterministic; it isn't in the code I was generating (even if I know it's pushing the right number of args). I was able to get around this because the compiler had enough information to unroll the loop (hence the constants in the generated IL) and thus produce a deterministic stack.

Problem

I'm working on a compiler using `System.Reflection.Emit`, and I'm getting JIT limitation errors I can't figure out. The problem occurs in my implementation of function handles. I.e. generating the code for ``` function foo() { } f = foo; f(); ``` Due to specifications beyond my control, the language is dynamically typed, so I can't know how many arguments `f` will expect at compile time. To counter this, rather than emitting a `Ldftn` for `foo`, I generate a new method, `λfoo`, that takes an array of the arguments given in the call expression and pushes them onto the eval stack for `foo`. Is that allowed in the CLR? What I get right now is a "JIT has encountered an internal limitation" exception (or "CLR has detected an invalid program" if I save the assembly and run it instead of calling it from memory) with a stack trace showing it happens in `λfoo`. This is the IL I'm generating. ``` .method private instance class [MylibInterop]MylibInterop.MylibValue 'λfoo'(class [MylibInterop]MylibInterop.MylibValue[] A_1) cil managed { // Code size 90 (0x5a) .maxstack 10 .locals init (int32 V_0, int32 V_1) IL_0000: ldarg.1 IL_0001: call instance int32 [mscorlib]System.Array::get_Length() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4 0x0 IL_000d: ble IL_001d IL_0012: ldstr "Too many arguments to lambda call" IL_0017: newobj instance void [mscorlib]System.Exception::.ctor(string) IL_001c: throw IL_001d: ldarg.0 IL_001e: ldc.i4.0 IL_001f: stloc.1 IL_0020: ldloc.0 IL_0021: newobj instance void [MylibInterop]MylibInterop.MylibValue::.ctor(int32) IL_0026: ldloc.1 IL_0027: ldloc.0 IL_0028: bge IL_003d IL_002d: ldarg.1 IL_002e: ldloc.1 IL_002f: ldelem [MylibInterop]MylibInterop.MylibValue IL_0034: ldloc.1 IL_0035: ldc.i4.1 IL_0036: add IL_0037: stloc.1 IL_0038: br IL_0026 IL_003d: ldloc.0 IL_003e: stloc.1 IL_003f: ldloc.1 IL_0040: ldc.i4 0x0 IL_0045: bge IL_0054 IL_004a: ldnull IL_004b: ldloc.1 IL_004c: ldc.i4.1 IL_004d: add IL_004e: stloc.1 IL_004f: br IL_003f IL_0054: call instance class [MylibInterop]MylibInterop.MylibValue debug.Program::foo(class [MylibInterop]MylibInterop.MylibValue) IL_0059: ret } // end of method Program::'λfoo' ```

Original source