Something about Resolution APIs

 

I haven't blogged for a long time because we were busy hitting ZBB for RTM milestone.

I am not aware of something special to talk about so let's talk about one of the main new features in Whidbey -- the token handle resolution APIs.

We have talked about tokens before and now it is more time for handles. We have RuntimeTypeHandle, RuntimeMethodHandle, RuntimeFieldHandle etc for each reflection entity. They contains a pointer to the real loaded managed image, for example, the RuntimeMethodHandle points to a MethodDesc which is used when invoking the method. In ReflectionOnly context (which disallows excecution), we disallowed the accessing to these handles simply because handles have a strong excution notion associated with them.

We provided a complete set of token -> handle -> info resolution APIs in Reflection. I am listing them below just for MethodInfo (or MethodBase), for Fields and Types, the APIs are similiar.

Token -> Info

public class Module

{

          public MethodBase ResolveMethod(int metadataToken);

 }

Info -> Token

public class MemberInfo

{

          public int MetadataToken { get; }

 }

Handle->Info 

public class MethodBase

{

         public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle);

}

Info->Handle

public class MethodInfo

{

         public RuntimeMethodHandle MethodHandle { get; }

 }

Token->Handle

public class ModuleHandle

{

          public RuntimeMethodHandle ResolveMethodHandle(int methodToken);

}

 

Let's talk about non-Generics first. Stories regarding Generics are quite complicated and worth another post. For non-Generics, Handle -- Info -- Token has a roughly one to one correspondence.

I said roughly because sometimes, it could the one to one correspondence could be broken. For example, the identity of a MethodInfo is determined by the Handle and the ReflectedType.

public class A

{

      public void M(){}

}

public class B:A

{}

MethodInfo mi1 = typeof(A).GetMethod("M");

MethodInfo mi2 = typeof(B).GetMethod("M");

 

The two MethodInfo has the same DeclaringType and the same RuntimeMethodHandle but they have different ReflectedType, so comparing the two of them, they won't be equal. Since their RuntimeMethodHandle are the same, invoking on them we are always invoking into the same method. So RuntimeMethodHandle really means an excution entity and MethodInfo could contain more information.

Token is completely Module based, so the same MethodInfo must have differnet tokens regarding to different modules. Within the same module, the tokens should have one to one correspondance to the MethodInfo if there is no Generic in the picture and the method is not a vararg method.

Why we want to provide the notion of Handles and Tokens?

By providing tokens, we are expanding reflect ability from purely string based into more possible dimensions (as shown in my examples in the first post). Token to Info resolution is faster than string->info resolutions (our PM joel had an MSDN article about the performance topic), and tokens occupy less space than infos. If you are to cache a lot of MethodInfos in your memory, it is going to be expensive. Instead you can get the tokens from info and cache those tokens instead. When you need to use the info, use the relative cheap token to info resolution to get the info back. Or you can cache the handles. Handles can even be used directly for invocation if you have the right type of delegate defined.

// Get the delegate's constructor

ConstructorInfo ctor = typeof(MyDelegate).GetConstructor(new Type[] {typeof(object), typeof(IntPtr)});

// invoke on the delegate
CACall del = (CACall)ctor.Invoke(new Object[] {null/*parameters to be pushed*/, myMethodHandle.GetFunctionPointer()});

 

 

Of course, the call above doesn't mean to save you much invocation time and working set. But it shows you how the handles means exceution entity. In the furture, we could possibliy provide more APIs to utilize the handles directly for managed code gen, but I cannot guarantee that now.

 

We provide a new type called MethodBody in Whidbey. So from a MethodInfo, you can get a MethodBody object, from the MethodBody you can get infos such as Local Variables, Exception Clauses and the IL byte array. Using the token resolution API and opcode.def file, you could easily parse the array to the re-assemble the opcodes. We could do all these in metadata API before but now we can do it through reflection APIs. In other word, you should be able to write a managed ildasm.exe now!