views:

176

answers:

2

First, here is the C# code and the disassembled IL:

public class Program<T>
{
    private List<T> _items;

    public Program(T x, [Microsoft.Scripting.ParamDictionary] Microsoft.Scripting.IAttributesCollection col)
    {
        _items = new List<T>();
        _items.Add(x);
    }
}

Here is the IL of that constructor:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor(!T x,
                             class [Microsoft.Scripting]Microsoft.Scripting.IAttributesCollection col) cil managed
{
  .param [2]
  .custom instance void [Microsoft.Scripting]Microsoft.Scripting.ParamDictionaryAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       34 (0x22)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  nop
  IL_0007:  nop
  IL_0008:  ldarg.0
  IL_0009:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<!T>::.ctor()
  IL_000e:  stfld      class [mscorlib]System.Collections.Generic.List`1<!0> class Foo.Program`1<!T>::_items
  IL_0013:  ldarg.0
  IL_0014:  ldfld      class [mscorlib]System.Collections.Generic.List`1<!0> class Foo.Program`1<!T>::_items
  IL_0019:  ldarg.1
  IL_001a:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<!T>::Add(!0)
  IL_001f:  nop
  IL_0020:  nop
  IL_0021:  ret
} // end of method Program`1::.ctor

I am trying to understand the IL code by emitting it myself. This is what I have managed to emit:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor(!T A_1,
                             class [Microsoft.Scripting]Microsoft.Scripting.IAttributesCollection A_2) cil managed
{
  // Code size       34 (0x22)
  .maxstack  4
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  ldarg.0
  IL_0007:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<!T>::.ctor()
  IL_000c:  stfld      class [mscorlib]System.Collections.Generic.List`1<!0> class MyType<!T>::_items
  IL_0011:  ldarg.0
  IL_0012:  ldfld      class [mscorlib]System.Collections.Generic.List`1<!0> class MyType<!T>::_items
  IL_0017:  ldarg.s    A_1
  IL_0019:  nop
  IL_001a:  nop
  IL_001b:  nop
  IL_001c:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<!T>::Add(!0)
  IL_0021:  ret
} // end of method MyType::.ctor

There are a few differences that I just can't figure out. I'm really close...

  1. How do I take care of the parameter attribute (ParamDictionaryAttribute)? I can't find a 'custom' opcode.

  2. Is the .param [2] important? How do I emit that?

  3. Why is the C# code stack size 8, while my emitted version is 4? Is this important?

+1  A: 

-> 1./2. Use DefineParameter() on the constructor builder (instead of defining them with the type[]), and then you can do a SetCustomAttribute() to apply the attribute to the parameter.

-> 3. That's not important I think. It basically specifies how much stack must be available for the method to be able to run.

Lucero
using SetCustomAttribute() on ctor will apply attrib to ctor, but it is applied to parameter, see my answer.
Andrey
Sorry, didn't realize that. Updated my answer (and +1 for you).
Lucero
+3  A: 

.custom is not opcode, it is the way to apply custom attribute. It is part of declaration. It is tightly bound with .param. .param[2] tells that now we will speak about 2nd parameter. .custom applies specified parameter. Take a look at MSIL spec, page 225 and 201 and 199 (for .maxstack)

To set custom attribute on parameter call DefineParameter on ctor and you get ParameterBuilder call SetCustomAttribute() on it

Andrey