Question

I am trying to generate a dynamic assembly using Reflection & Emit in .NET. I am getting an error, "Common Language Runtime detected an invalid program." I created another program which has the functionality I want using hard-coded types. The functionality I am trying to write will ultimately use dynamic types, but I can use ILDasm to see the IL I need to generate. I am comparing the IL I am generating with the IL which the compiler generates. In the .locals init declaration of one method I see there is an extra item in the compiler-generated code,

compiler-generated:

.locals init ([0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
           [1] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0001)

mine:

.locals init (class [System.Core]System.Linq.Expressions.ParameterExpression V_0,  
       class [System.Core]System.Linq.Expressions.ParameterExpression[] V_1)

I don't understand the significance of the "[0]" and "[1]" in the compiler-generated code. Can anyone tell me what it means?

As a more general question, I can follow most ILDasm output without too much trouble. But every so often I run across a problematic expression. For instance, in this line from ILDasm

callvirt   instance class [EntityFramework]System.Data.Entity.ModelConfiguration.EntityTypeConfiguration`1<!!0> [EntityFramework]System.Data.Entity.DbModelBuilder::Entity<class DynamicEdmxTrial.HardFooAsset>()

the "!!0" probably refers to the generic type of the Entity<>, but I don't know for sure, and I wonder if there is a key to ILDasm output that would explain its more obscure output to me.

Was it helpful?

Solution

The specification is freely available here. It takes a little getting used to, but most details are easily found once you figure out the structure.

!! is listed in II.7.1 Types:

Type ::=       | Description                             | Clause
  ‘!’ Int32    | Generic parameter in a type definition, | §II.9.1
               | accessed by index from 0                |
| ‘!!’ Int32   | Generic parameter in a method           | §II.9.2
               | definition, accessed by index from 0    |
...

In other words, inside a method that C# would call f<T, U>(), !!0 would be T, and !!1 would be U.

However, the [0] is a good question. The spec does not seem to address it. The .locals directive is described in II.15.4.1.3 The .locals directive, which lists the syntax as

MethodBodyItem ::= ...
 | .locals [ init ] ‘(’ LocalsSignature ‘)’
LocalsSignature ::= Local [ ‘,’ Local ]*
Local ::= Type [ Id ]

There is nothing that seems to allow [0] there unless it is part of a Type, and Type does not allow anything starting with [ either. My guess is that this is an undocumented peculiarity specific to Microsoft's implementation, intended to help the human reader see that location 0 is local variable CS$0$0000, for when the generated instructions access local variables by index.

Experimenting with ILAsm shows that this is exactly what it means. Taking a simple C# program:

static class Program {
    static void Main() {
        int i = 0, j = 1;
    }
}

and compiling and then disassembling it (csc test.cs && ildasm /text test.exe >test.il) shows:

....
.locals init (int32 V_0,
         int32 V_1)
IL_0000:  nop
IL_0001:  ldc.i4.0
IL_0002:  stloc.0
IL_0003:  ldc.i4.1
IL_0004:  stloc.1
IL_0005:  ret
....

Modifying the .locals to

.locals init ([0] int32 V_0, [0] int32 V_1)

gives a useful warning message:

test.il(41) : warning : Local var slot 0 is in use

And indeed, declaring variables of different types, then reordering them using [2], [1], [0], assembling and immediately disassembling the result, shows that the variables got reordered.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top