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.