Managed CodeGen

This blog is to introduce some new features about Managed CodeGen in Whidbey. We provided a new way of doing LightWeight CodeGen, added support for emitting Generics in Reflection.Emit and there is some new exciting token handle stories in Reflection going on.

  • TypeName Grammar

    I was about to post another blog regarding to tokens and handles and generics, but our Reflection tester Haibo suggested me to talk a little about the TypeName Grammar that we added in Whidbey because a lot of external bugs are being reported on this issue. 

     

    Let's start from a bug:

    Type.GetType("System.Drawing.Font") returns null

     

    We can get the type of System.Object through Type.GetType(“System.Object”) but why we cannot get the type of System.Drawing.Fone? It simply because all we know is to look up your executing assembly and mscorlib for the types specified with simple name and we couldn't find the type because System.Drawing.Font is in System.Drawing.dll.

    You will have to pass in the type name plus the assembly name in order for us to load the assembly then look for the type in that asssembly.

     

    To make your program robust and being able to always get the Type back, you should be using the type's AssemblyFullyQualifiedName to get the type back. The easiest way to know a the AssemblyFullQualifiedName is to call t.AssemblyFullyQualifiedName.

     

    For example, System.Drawing.Font  type’s AFQN is:

    System.Drawing.Font, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

     

    The rule behind it is:

    t.FullName + “,” + asm.FullName = t.AFQN

     

    For Generic Types, things become more complicated. The simplest way is still to call t. AssemblyFullyQualifiedName to see what is the right string to be used in Type.GetType.

     

    A simple Generic Type G<Object, String> ’s AQFN may look like this:

    G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

     

    It looks pretty complicated but to break it up, it is reasonable.

    We cannot use single “[“ because then we will not know when is the start of a new Generic Parameter (they are all commas).

    Calling

    Type.GetType(“G`2[[System.Object],[System.String]]”) will return you the type because in this case, G, System.Object and System.String all reside in the default look up scope. If any of these types are not in the excuting assembly or mscorlib, then you will have to specify the AQFN of that type.

    In our case, all the following names will retrieve the type back for you.

     

    G`2[[System.Object, mscorlib],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

    G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String]]

    G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

     

    So what should be the G<Object, String>'s full name? According to the rule above, it should be:

    G`2[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

     

    This fullname does contain some assembly infomation which seem to be a little strange to some users. But that assembly infomation is about the Generic Arguments and not about the type itself. We need them because Generic Arguments in live in a different assembly as the Generic Type Definition.

     

    If you want to use Type.GetType to get back G<object>, the string to use is G[[System.Object]]. Again double "[" here.

     

    If you want to automatically compose/parse these type names on large scales without having the type first, we provided a set of TypeNameBuilder and TypeNameParser APIs through mscoree.tlb. You can consume them either in unmanaged code or from managed side through interop.

     

    Here is a small sample of using the API from managed side:

     

    Tlbimp mscoree.tlb /out=mscoree_me.dll

    using System;

    using System.Reflection;

    using System.Runtime.InteropServices;

    using mscoree_mm;

     

    public class TypeNameBuilderAPITest

    {

                public static void Main()

                {

                           

                            ITypeNameFactory tf = new TypeNameFactoryClass();

                            ITypeNameBuilder tb = tf.GetTypeNameBuilder();

     

                            tb.Clear();

                            tb.AddName("G");

                            tb.OpenGenericArguments();

                            tb.OpenGenericArgument();

                            tb.AddName("T");

                            tb.AddAssemblySpec("Asm");

                            tb.CloseGenericArgument();

                            tb.CloseGenericArguments();

                            tb.AddSzArray();        

                            Console.WriteLine(tb.ToString());

                           

                            uint Epos = 0; // error position

                            ITypeName tn = tf.ParseTypeName(tb.ToString(), out Epos);

                            Console.WriteLine(tn.GetTypeArgumentCount());

    }

    }

     

    Output:

    G[[T, Asm]][]

    1

     

    The last note is that, for Type.GetType(String typeName) if the typeName is correct in the grammar but we cannot find the type, we will return null on this API. If the type name grammar is incorrect, ArgumentException will be thrown.

     

     

  • Token APIs on Module Builder

    We have some token APIs on Module Builder that can be used to as an optional approach as to regular Refelction.Emit APIs.

    They are:

    public TypeToken GetTypeToken(Type type)

    public TypeToken GetTypeToken(String name) // this looks just like ModuleBuilder.GetType(string).

    public MethodToken GetMethodToken(MethodInfo method)

    public MethodToken GetArrayMethodToken(Type arrayClass, String methodName, CallingConventions callingConvention, Type returnType, Type[] parameterTypes)

    public MethodToken GetConstructorToken(ConstructorInfo con)

    public FieldToken GetFieldToken(FieldInfo field)

    public StringToken GetStringConstant(String str)

    public SignatureToken GetSignatureToken(SignatureHelper sigHelper)

    public SignatureToken GetSignatureToken(byte[] sigBytes, int sigLength)

    Here is a code sample showing you the useage as GetArrayMethod for referenced Generic Methods:

    using System.Reflection;
    using System.Reflection.Emit;
    using System;

    public class Test
    {
        public static void Main()
        {
            AssemblyBuilder asmb = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("C"), AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder modb = asmb.DefineDynamicModule("C.dll");
            TypeBuilder A = modb.DefineType("A", TypeAttributes.Public);
            MethodBuilder AM = A.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[]{});
            ILGenerator ilgen = AM.GetILGenerator();
            MethodToken mtToken = modb.GetMethodToken(typeof(G<int>).GetMethod("Foo"));
            LocalBuilder loc = ilgen.DeclareLocal(typeof(G<int>));
            ilgen.Emit(OpCodes.Ldnull);
            ilgen.Emit(OpCodes.Call, mtToken.Token);
            ilgen.Emit(OpCodes.Ldc_I4, 0);
            ilgen.Emit(OpCodes.Ret);
            A.CreateType();
            asmb.Save("C.dll");
        }
    }
    public class G<T>
    {

        public static void Foo(G<T> foo)
        {
        }
    }

    The code we emitted look like this:

    .method public static int32  M() cil managed
    {
      // Code size       12 (0xc)
      .maxstack  2
      .locals init (class [repro]G`1<int32> V_0)
      IL_0000:  ldnull
      IL_0001:  call       void class [repro]G`1<int32>::Foo(class [repro]G`1<!0>)
      IL_0006:  ldc.i4     0x0
      IL_000b:  ret
    } // end of method A::M

    Due to some bugs in Reflection in Beta2, a similiar emitting using normal TypeBuilder.GetMethod won't work in this case. But by using the token APIs, we work directly on module builders and don't need to go through Reflection and thus can correctly emit the signature. It could be also memeory saving for you as well if you just cache the token instead of caching the method info during emitting, you can probably make your GC heap smaller.

     

  • How to Emit Generics

    Reflection.Emit can emit Generics! Checkout here:

    Here is a code sample that tells you how to emit various Generics. It is created as dev unit test by Chris King who implemented Reflection.Emit on Generics.

    using System;
    using System.Reflection;
    using System.Reflection.Emit;

    public class G<T>  // this class is going to be defined in instance()
    {
        public static T F;
        public static bool B;

        public static void Foo()
        {
            bool b = B;

            // Case(peverify bug): typeDef G<T> -> methodDef G<T>.Foo()
            // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>.Foo()
            Foo();

            // Case: typeDef G<T> -> typeSpec G<object> -> memberRef G<T>.Foo()
            G<object>.Foo();

            // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>()
            new G<T>();

            new G<object>();
        }

        public static void M<S>()
        {
            T t = F;
            S s = default(S);

            object boxVar = t;

            object boxMVar = s;

            // Case: typedef G<T> -> typeSpec G<object> -> memberRef G<object>.M<S>() -> methodSpec G<object>.M<string>()
            G<object>.M<string>();

            // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>.M<S>() -> methodSpec G<T>.M<string>()
            M<string>();

            // Case: typeDef G<T> -> typeSpec G<T> -> memberRef G<T>.M<S>() -> methodSpec G<T>.M<S>()
            M<S>();

            // Case: typeDef G<T> -> typeSpec G<object> -> memberRef G<object>.M<S>() -> methodSpec G<T>.M<S>()
            G<object>.M<S>();
        }
    }

    public class Entry
    {
        public static int Main()
        {
            Instance();
            Reference();
            Constrained();

            return 100;
        }

        public static void Instance()
        {
            AssemblyName name = new AssemblyName("Instance");
            AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder mod = asmbuild.DefineDynamicModule("Instance.exe");

            TypeBuilder G = mod.DefineType("G", TypeAttributes.Public);
            Type T = G.DefineGenericParameters("T")[0];
            Type GObj = G.MakeGenericType(new Type[] { typeof(object) });

            FieldBuilder F = G.DefineField("F", T, FieldAttributes.Public);
            FieldBuilder B = G.DefineField("B", typeof(bool), FieldAttributes.Public | FieldAttributes.Static);

            ConstructorBuilder Ctor = G.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
            {
                ILGenerator il = Ctor.GetILGenerator();
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
                il.Emit(OpCodes.Ret);
            }

            MethodBuilder Foo = G.DefineMethod("Foo", MethodAttributes.Public);
            {
                // G<object>.Foo()
                MethodInfo GObjFoo = TypeBuilder.GetMethod(GObj, Foo);

                // new G<object>()
                ConstructorInfo GObjCtor = TypeBuilder.GetConstructor(GObj, Ctor);

                ILGenerator il = Foo.GetILGenerator();
                CompileButDontRunMe(il, B);

                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, F);
                il.Emit(OpCodes.Box, T);
                il.Emit(OpCodes.Pop);

                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Call, Foo); // G<T>.Foo()
                il.Emit(OpCodes.Ldnull);
                il.EmitCall(OpCodes.Call, Foo, null);

                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Call, GObjFoo); // G<object>.Foo()
                il.Emit(OpCodes.Ldnull);
                il.EmitCall(OpCodes.Call, GObjFoo, null);

                il.Emit(OpCodes.Newobj, Ctor);  // new G<T>()          
                il.Emit(OpCodes.Pop);

                il.Emit(OpCodes.Newobj, GObjCtor); // new G<object>()
                il.Emit(OpCodes.Pop);

                il.Emit(OpCodes.Ret);
            }

            MethodBuilder M = G.DefineMethod("M", MethodAttributes.Public);
            {
                Type S = M.DefineGenericParameters("S")[0];

                // G<object>.M<S>
                MethodInfo GObjM = TypeBuilder.GetMethod(GObj, M);

                // G<object>.M<string>()
                MethodInfo GObjMStr = GObjM.MakeGenericMethod(typeof(string));

                // G<T>.M<string>()
                MethodInfo GMStr = M.MakeGenericMethod(typeof(string));

                ILGenerator il = M.GetILGenerator();
                CompileButDontRunMe(il, B);
                il.DeclareLocal(S);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldloc_1);
                il.Emit(OpCodes.Box, S);
                il.Emit(OpCodes.Stloc, il.DeclareLocal(typeof(object)));
                il.Emit(OpCodes.Pop);
                il.DeclareLocal(T);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, F);
                il.Emit(OpCodes.Stloc_0);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Call, GObjMStr); // G<object>.M<string>()
                il.Emit(OpCodes.Ldnull);
                il.EmitCall(OpCodes.Call, GObjMStr, null); // G<object>.M<string>()
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Call, GMStr); // G<T>.M<string>()
                il.Emit(OpCodes.Ldnull);
                il.EmitCall(OpCodes.Call, GMStr, null); // G<T>.M<string>()
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Call, M); // G<T>.M<S>()
                il.Emit(OpCodes.Ldnull);
                il.EmitCall(OpCodes.Call, M, null); // G<T>.M<S>()
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Call, GObjM); // G<object>.M<S>
                il.Emit(OpCodes.Ldnull);
                il.EmitCall(OpCodes.Call, GObjM, null); // G<object>.M<S>
                il.Emit(OpCodes.Ret);
            }

            Type rtG = G.CreateType();

            Console.WriteLine("Instance.exe");
            asmbuild.Save("Instance.exe");

            object target = Activator.CreateInstance(rtG.MakeGenericType(typeof(object)), null);
            rtG.MakeGenericType(typeof(object)).InvokeMember("Foo", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, target, null);
            rtG.MakeGenericType(typeof(object)).GetMethod("M").MakeGenericMethod(typeof(string)).Invoke(target, null);
        }

      
        public static void Reference()
        {
            AssemblyName name = new AssemblyName("Reference");
            AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder mod = asmbuild.DefineDynamicModule("Reference.exe");

            TypeBuilder H = mod.DefineType("H", TypeAttributes.Public);
            Type U = H.DefineGenericParameters("U")[0];
            Type G = typeof(G<>);
            Type GObj = typeof(G<object>);
            Type GU = G.MakeGenericType(U);

            FieldBuilder B = H.DefineField("B", typeof(bool), FieldAttributes.Public | FieldAttributes.Static);

            MethodBuilder Baz = H.DefineMethod("Baz", MethodAttributes.Static | MethodAttributes.Public);
            {
                // G<object>.Foo()
                MethodInfo GObjFoo = GObj.GetMethod("Foo");

                // G<object>.M<object>()
                MethodInfo GObjMObj = GObj.GetMethod("M").MakeGenericMethod(typeof(object));

                // G<U>.Foo()
                MethodInfo GFoo = G.GetMethod("Foo");
                MethodInfo GUFoo = TypeBuilder.GetMethod(GU, GFoo);

                // G<U>.M<U>()
                MethodInfo GM = G.GetMethod("M");
                MethodInfo GUM = TypeBuilder.GetMethod(GU, GM);
                MethodInfo GUMU = GUM.MakeGenericMethod(U);

                // G<object>.M<U>
                MethodInfo GObjM = GObj.GetMethod("M");
                MethodInfo GObjMU = GObjM.MakeGenericMethod(U);

                // new G<object>()
                ConstructorInfo GObjCtor = GObj.GetConstructor(new Type[0]);

                // G<U>()
                ConstructorInfo GCtor = G.GetConstructor(new Type[0]);
                ConstructorInfo GUCtor = TypeBuilder.GetConstructor(GU, GCtor);

                // G<U>.F
                FieldInfo GF = G.GetField("F");
                FieldInfo GUF = TypeBuilder.GetField(GU, GF);

                // G<U>.B
                FieldInfo GB = G.GetField("B");
                FieldInfo GUB = TypeBuilder.GetField(GU, GB);

                // Nullable<int>()
                Type Nullable = typeof(Nullable<>);
                Type NullableInt = Nullable.MakeGenericType(typeof(int));
                ConstructorInfo NullableIntCtor = NullableInt.GetConstructor(new Type[] { typeof(int) });

                ILGenerator il = Baz.GetILGenerator();
                il.Emit(OpCodes.Ldloca, il.DeclareLocal(NullableInt));
                il.Emit(OpCodes.Ldc_I4_3);
                il.Emit(OpCodes.Call, NullableIntCtor);
                CompileButDontRunMe(il, B);
                il.Emit(OpCodes.Ldsfld, GUF); // G<U>.F
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldsfld, GUB); // G<U>.B
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Call, GObjFoo); // G<object>.Foo()
                il.Emit(OpCodes.Call, GObjMU); // G<object>.M<U>()
                il.Emit(OpCodes.Newobj, GObjCtor); // new G<object>()
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Newobj, GUCtor); // new G<U>()
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Call, GUFoo); // G<U>.Foo()
                il.Emit(OpCodes.Call, GUMU); // G<U>.M<U>
                il.Emit(OpCodes.Call, GObjMObj); // G<object>.M<object>()
                il.Emit(OpCodes.Ret);
            }

            Type rtH = H.CreateType();

            Console.WriteLine("Reference.exe");
            asmbuild.Save("Reference.exe");

            rtH.MakeGenericType(typeof(object)).InvokeMember("Baz", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null);
        }

        public static void Constrained()
        {
            AssemblyName name = new AssemblyName("Constrained");
            AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder mod = asmbuild.DefineDynamicModule("Constrained.exe");

            TypeBuilder IFoo = mod.DefineType("IFoo", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
            MethodBuilder IFooM = IFoo.DefineMethod("IFooM", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract);

            TypeBuilder G = mod.DefineType("G", TypeAttributes.Public);
            GenericTypeParameterBuilder T = G.DefineGenericParameters("T")[0];
            T.SetInterfaceConstraints(IFoo);
            Type GObj = G.MakeGenericType(new Type[] { typeof(object) });

            FieldBuilder F = G.DefineField("F", T, FieldAttributes.Public | FieldAttributes.Static);
            FieldBuilder B = G.DefineField("B", typeof(bool), FieldAttributes.Public | FieldAttributes.Static);

            MethodBuilder Foo = G.DefineMethod("Foo", MethodAttributes.Static | MethodAttributes.Public);
            {
                ILGenerator il = Foo.GetILGenerator();
                CompileButDontRunMe(il, B);
                il.Emit(OpCodes.Ldsflda, F);
                il.Emit(OpCodes.Constrained, T);
                il.Emit(OpCodes.Callvirt, IFooM);
                il.Emit(OpCodes.Ret);
            }

            Type rtIFoo = IFoo.CreateType();
            Type rtG = G.CreateType();

            Console.WriteLine("Constrained.exe");
            asmbuild.Save("Constrained.exe");

            rtG.MakeGenericType(rtIFoo).InvokeMember("Foo", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null);
        }

        public static void CompileButDontRunMe(ILGenerator il, FieldBuilder B)
        {
            il.Emit(OpCodes.Ldsfld, B);
            Label label = il.DefineLabel();
            il.Emit(OpCodes.Brtrue_S, label);
            il.Emit(OpCodes.Ret);
            il.MarkLabel(label);
        }
    }

     

     

  • Token Resolution (I)

    This is my first post. Let me start by introducing a small program that you can do with Whidbey Reflection.

    Here is a small app showing you how you can get all the methods in Assembly A that is referenced by Assembly B. There was no API such as GetReferencedMethods before in Refleciton.

    using System;
    using System.Reflection;

    public class GetRef
    {
        public static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Usage : GetRef hostAsm userAsm");
                return;
            }
            Assembly HostAsm = Assembly.Load(args[0]);
            Assembly UserAsm = Assembly.Load(args[1]);
            Module[] mods = UserAsm.GetModules();
            int c = 1;
            foreach (Module mod in mods)
            {
                Console.WriteLine("Wroking on Module : " + mod);
                try
                {
                    while (true)
                    {
                        MemberInfo mi = mod.ResolveMember(0x0A000000 | (c++)); // in CLI specification, all member ref tokens start with 0x0A
                        if (mi.Module.Assembly.FullName == HostAsm.FullName)
                            Console.WriteLine("[" + mi.Module.Assembly + "] " + mi);
                    }
                }
                catch (ArgumentOutOfRangeException) // when we token gets out of range of what the assembly have, we throw the exception
                {
                }
            }
        }
    }

    Usage:

    getref mscorlib b28423_ILGeneratorNull
    Wroking on Module : b28423_ILGeneratorNull.exe
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] Void .ctor(System.String)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] Void .ctor(DebuggingModes)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] Void .ctor(Int32)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] System.Type GetTypeFromHandle(System.RuntimeTypeHandle)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] System.Reflection.Module get_Module()
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] Void .ctor(System.String, System.Type, System.Type[], System.Reflection.Module, Boolean)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] System.Delegate CreateDelegate(System.Type)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] Void WriteLine(System.String)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] System.String ToString()
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] System.String Concat(System.String, System.String)
    [mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089] Void .ctor()

    And it is fast, simple and without duplications!

    A token is the offset to the metadata table in an module. In a given assembly, we have many token tables for different entities, and here is a list of intersting token tables:

    MemberRef : 0x0A

    MethodDef : 0x06

    MethodSpec : 0x2B

    TypeDef : 0x02

    TypeRef : 0x01

    TypeSpec : 0x1B

    Compile the following small program:

    using System;
    public class A
    {
        public static void Main()
        {
            new B<int>().M<String>("Test");
        }
    }
    public class B<T>
    {
        public void M<Z>(Z z)
        {
            Console.WriteLine(z);
        }
    }

    csc A.cs

    ildasm A.exe

    and press "Ctrl+M" inside ildasm, you can see all the metadata tables from there.

    For example, Type A is a type Def token with offset 2. The token for type A is then 02000002.

    A Type Spec means the instantiated generic type used in the assembly. We have B<int>, and its token is 1b000001, while B<T> is a TypeDef whose token is 02000003.

    Please note that how to arrange tokens is a decision made by compilers. Compilers don't necessary put consecutive tokens (although I can hardly see why they won't do it) or let the tokens start from 1. For example, I found that in C# the type def token always starts with 2. Also if one program is compiled twice, it is likely the tokens inside will be rearranged. For example, it is possible that the next time I compile the program B<int> becomes 02000002 and type A becomes 02000003. So token is not a reliable way to cache type identity across runs. But once the assembly is loaded, the tokens should be fixed. So during the run, it is safe to think B<int>.MetaDataToken will never change.

     

     

More Posts « Previous page

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker