I don’t want another argument

Having had a very brief look at the compilation of generic classes, I thought it would be good to see how generic methods are handled by the CLR. The following example contains a TestMethod specialized on a generic parameter.

    class Program
    {
        class S<T>
        {
            public S(T x)
            {
                Console.WriteLine("start" + x);
            }
        }
        void TestMethod<T>(T x)
        {
            S<T> a = new S<T>(x);
            S<T> b = new S<T>(x);
        }
        static void Main()
        {
            object foo = new object();
            Program z = new Program();
            z.TestMethod(foo);
            z.TestMethod(foo);
            z.TestMethod("2");
        }
    }

If we look at the first call to TestMethod in Main we see it has the code

            z.TestMethod(foo);
00000069  push        979938h
0000006e  mov         edx,dword ptr [ebp-3Ch]
00000071  mov         ecx,dword ptr [ebp-40h]
00000074  cmp         dword ptr [ecx],ecx
00000076  call        FD2BB058
0000007b  nop          

The surprising thing is that an extra argument, 979938h, is being passed into the method. Why do we need the extra method parameters? Well, the body of TestMethod creates a new instance, and in order to do this we need a type handle for the newly created type. The purpose of this extra argument is to cache the handles for new instances.

At the point of instantiation in the TestMethod, we can see how the this table is used.

            S<T> a = new S<T>(x);
00000043  mov         eax,dword ptr [ebp+8]
00000046  mov         eax,dword ptr [eax+0Ch]
00000049  add         eax,4
0000004c  mov         dword ptr [ebp-50h],eax
0000004f  mov         eax,dword ptr [ebp-50h]
00000052  mov         eax,dword ptr [eax]
00000054  mov         dword ptr [ebp-54h],eax
00000057  cmp         dword ptr [ebp-54h],0
0000005b  jne         00000073
0000005d  push        0   
0000005f  push        dword ptr [ebp-50h]
00000062  push        0   
00000064  mov         ecx,dword ptr [ebp+8]
00000067  mov         edx,1B000001h
0000006c  call        7689C368
00000071  jmp         00000076
00000073  mov         eax,dword ptr [ebp-54h]
00000076  mov         ecx,eax
00000078  call        FD2A09CC
0000007d  mov         dword ptr [ebp-4Ch],eax
00000080  mov         edx,dword ptr [ebp-40h]
00000083  mov         ecx,dword ptr [ebp-4Ch]
00000086  call        FD2BAFA0
0000008b  mov         eax,dword ptr [ebp-4Ch]
0000008e  mov         dword ptr [ebp-44h],eax

After the load at line 43, we have eax containing 

0x00979938  43000001 00050004 009798f8 00979914

and the 0xC offset points to the table. Here 7933061c is the handle for the object type.

0x00979914  7933061c 00000000 00000000 00000000

By the time we get to 76, the table has been filled with another type handle by the call at 6c.

0x00979914  7933061c 00979ad8 00000000 00000000

By the time we get to 7D, we have constructed the new object on the heap, using the handle from the table as the header

0x01394D14  00979ad8 00000000

And we are now ready to call the constructor at 86.

Next time we go through the code, we lift the type handle from the table without the need to call any code to fill it. Zero is used to represent an unfilled entry with the conditional code at 53 using any previously calculated value.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s