Hi
I'm having a problem retrieving and parsing the signature of a mdtTypeDef token. My metadata knowledge is not the best, so for all I know I might be going about this in the completely wrong way.
I've read the metadata specification (Partition II of the CLI spec) and they actually have an example which is very similar to what I'm trying to do. I quote from page 495 of ECMA specification 335 where they have this sample code:
using System;
class Phone<K, V>
{
private int hi = -1;
private K[] keys;
private V[] vals;
public Phone() { keys = new K[10]; vals = new V[10]; }
public void Add(K k, V v) { keys[++hi] = k; vals[hi] = v; }
}
class App
{
static void AddOne<KK, VV>(Phone<KK, VV> phone, KK kk, VV vv)
{
phone.Add(kk, vv);
}
static void Main()
{
Phone<string, int> d = new Phone<string, int>();
d.Add("Jim", 7);
AddOne(d, "Joe", 8);
}
}
The spec continues to define the metadata of the above example:
Following this production, the Phone<string,int> instantiation above is encoded as:
0x15 ELEMENT_TYPE_GENERICINST
0x12 ELEMENT_TYPE_CLASS
0x08 TypeDefOrRef coded index for class “Phone<K,V>”
0x02 GenArgCount = 2
0x0E ELEMENT_TYPE_STRING
0x08 ELEMENT_TYPE_I4
...
Similarly, the signature for the (rather contrived) static method AddOne is encoded as:
0x10 IMAGE_CEE_CS_CALLCONV_GENERIC
0x02 GenParamCount = 2 (2 generic parameters for this method: KK and VV
0x03 ParamCount = 3 (phone, kk and vv)
0x01 RetType = ELEMENT_TYPE_VOID
0x15 Param-0: ELEMENT_TYPE_GENERICINST
0x12 ELEMENT_TYPE_CLASS
0x08 TypeDefOrRef coded index for class “Phone<KK,VV>”
0x02 GenArgCount = 2
0x1e ELEMENT_TYPE_MVAR
0x00 !!0 (KK in AddOne<KK,VV>)
0x1e ELEMENT_TYPE_MVAR
0x01 !!1 (VV in AddOne<KK,VV>)
0x1e Param-1 ELEMENT_TYPE_MVAR
0x00 !!0 (KK in AddOne<KK,VV>)
0x1e Param-2 ELEMENT_TYPE_MVAR
0x01 !!1 (VV in AddOne<KK,VV>)
I have a very similar situation to the one above: a class with 2 generic parameters (similar to Phone) and a method that references the generic parameters of class (just likePhone.Add). Note: I'm not talking about App.AddOne, I'm talking about Phone.Add (although the metadate for AddOne is almost the same as Phone.Add).
I have the mdtMethodDef token for a method. I call IMetaDataImport.GetMethodProps to get the methods binary signature. I can successfully parse the signature of the method. My problems started when I hit the parameters section of the blob. The generic parameters
(e.g. "k" and "v" of Phone.Add) are defined as ELEMENT_TYPE_GENERICINST.
ELEMENT_TYPE_GENERICINST is defiend as:
Generic type instantiation. Followed by type type-arg-count type-1 ... type-n
I parsed the type of the GENERICINST and found ELEMENT_TYPE_VAR, defined as:
Generic parameter in a generic type definition, represented as number (compressed unsigned integer)
At first I had no idea what I was supposed to do with the "number" referenced by ELEMENT_TYPE_VAR, but after much searching and a bit of luck I discovered it referenced the generic parameters of the implementing class of the method i.e. "k"
is a GENERICINST of type VAR referencing generic parameter 1 of it's containing class "Phone". (if it weren't for a lot of luck in finding the example I quoted above I would never have figured this out).
This is where I reach my next problem: how do I get to the generic parameters of the containing class? IMetaDataImport::GetMethodProps returns "pClass : [out] A Pointer to a TypeDef
token that represents the type that implements the method". I can use "pClass" and callIMetaDataImport2::EnumGenericParamswhich works correctly. The only problem is that I don't know how many generic parameters there are, so I have to call EnumGenericParams repeatedly, asking for 1 parameter each time until it fails. That might be the way it's supposed to be done, but when
I enum the generic parameters of a mdtMethodDef I can get them all at once because the methodDef signature includes the number of generic parameters that the method has.
So that got me wondering about what other information I can get about the mdtTypeDef token I have for the implementing class. I triedIMetaDataImport::GetTypeDefProps, but that really doesn't return anything I need at the moment, I can't even tellif there are generic parameters never mind how many.
I searched through cor.h and CorHdr.h to see where else I could use a mdtTypeDef token, but I only found one method that looked useful:IMetaDataImport::GetSigFromToken
My theory is that I can use GetSigFromToken() to get the signature of the implementing class. I tried the call and it returned successfully. The signature returned was 4 bytes. If you refer back to the example I mentioned at the beginning you'll notice that
the metadata for "Phone" indicated it was a ELEMENT_TYPE_GENERICINST of type ELEMENT_TYPE_CLASS with 2 generic arguments: ELEMENT_TYPE_STRING and ELEMENT_TYPE_I4. I'm expecting to see that kind of information embedded in the signature of the TypeDef?
At the moment I have what I think is the signature of the implementing class, but I have no idea what to do with it. I expect it'll look similar (if not the same) as the signature for a TypeSpec (Partition II 23.2.14) so I thought it would start with the
element type i.e. ELEMENT_TYPE_GENERICINST, but it doesn't. If I uncompress the element type I end up with U2. I thought maybe it started with the length, but that's not right either, since the call to GetSigFromToken() returned 4 bytes.
So my questions are:
1. How do I determine the types of the generic arguments of a method when they are ELEMENT_TYPE_VARs? Do I just enumerate the generic parameters of the implementing class one by one until there are none left? Or can I determine upfront how many there are?
2. Am I right in attempting to lookup the signature of the mdtTypeDef token I have for the implementing class? If so how do I decode it?
Sorry in advance if this question doesn't make sense, it's not the easiest of things to explain.
Cheers
Greg