Program30.csc.txt

public string GetElementType ( int index , byte [] blobarray , out int howmanybytes)

{

howmanybytes = 0;

string returnstring = "";

byte type = blobarray[index];

if ( type == 0x0f)

returnstring = GetPointerToken(index, blobarray , out howmanybytes);

return returnstring;

}

 

public string GetPointerToken(int index , byte [] blobarray , out int howmanybytes)

{

string returnstring = "";

int howmanybytes2;

returnstring = GetElementType(index+1 , blobarray , out howmanybytes2) + "*";

howmanybytes = howmanybytes2 + 1;

return returnstring;

}

 

e.il

.class zzz

{

.method  int32 *** a1()

{

}

.method  class zzz ****** a2()

{

}

.method   int32  [12][3,5] *  a3()

{

}

}

 

Output

  .method /*06000001*/   int32***

  .method /*06000002*/   class zzz/* 02000002 */******

  .method /*06000003*/   int32[12][3,5]*

 

In the above il file we have added a unmanaged pointer * like we added a managed pointer & in the earlier example. There is not much change in from the earlier program.

 

The only difference is that the unmanaged pointer has a type of 0x0f and in the il file we have shown how recursion really works by adding as many stars as we could and yet demonstrate that the program works. Thus the short explanation.

 

Program31.csc

public string DecodeToken (int token , int type)

{

byte tabletype = (byte)(token & 0x03);

int tableindex = token >> 2;

string returnstring = "";

if ( tabletype == 0)

returnstring = typedefnames[tableindex];

if ( tabletype == 1 )

returnstring = typerefnames[tableindex] ;

return returnstring;

}

public string GetTableRefNameForFillArray( int tablerow , int typerow , string tablename)

{

string nameandnamespace = "";

if ( tablename == "AssemblyRef")

nameandnamespace = NameReserved(GetString(AssemblyRefStruct[tablerow].name)) ;

if ( tablename == "ModuleRef")

nameandnamespace = ".module " + NameReserved(GetString(ModuleRefStruct[tablerow].name)) ;

string stringnamespace = NameReserved(GetString(TypeRefStruct[typerow].nspace)) ;

string stringnested  = "";

if ( stringnamespace != "")

stringnamespace  = stringnamespace + ".";

if ( tablename == "AssemblyRef")

stringnested  = "[" + nameandnamespace  + "/* 23" + tablerow.ToString("X6") + " */]" + stringnamespace + NameReserved(GetString(TypeRefStruct[typerow].name));

if ( tablename == "ModuleRef")

stringnested  = "[" + nameandnamespace + "/* 1A" + tablerow.ToString("X6") + " */]" + stringnamespace + NameReserved(GetString(TypeRefStruct[typerow].name));

if ( tablename == "Module")

stringnested  = stringnested  + stringnamespace + NameReserved(GetString(TypeRefStruct[typerow].name));

stringnested  = stringnested  + "/* 01" + typerow.ToString("X6") + " *//" ;

return stringnested;

}

public string GetTypeRefForFillarray(int k)

{

string stringnamespace = NameReserved(GetString(TypeRefStruct[k].nspace)) ;

if ( stringnamespace != "")

stringnamespace= stringnamespace + ".";

string stringnested  = stringnamespace + NameReserved(GetString(TypeRefStruct[k].name)) ;

stringnested  = stringnested  + "/* 01" + k.ToString("X6") + " */" ;

return stringnested;

}

string [] typerefnames;

string [] typedefnames;

public void FillArray ()

{

int old = tableoffset;

bool tablehasrows  = tablepresent(1);

int offs = tableoffset;

tableoffset = old;

if ( tablehasrows )

{

typerefnames = new string[rows[1]+1];

for ( int k = 1 ; k <= rows[1] ; k++)

{

short resolutionscope = BitConverter.ToInt16 (metadata , offs);

offs = offs + 2;

int name = ReadStringIndex(metadata , offs);

offs = offs + offsetstring;

int nspace = ReadStringIndex(metadata , offs);

offs = offs + offsetstring;

string stringname = NameReserved(GetString(name));

string stringtypefminusnested = GetString(nspace) ;

if ( stringtypefminusnested .Length != 0)

stringtypefminusnested = stringtypefminusnested + ".";

stringtypefminusnested = stringtypefminusnested + stringname;

string stringnested = "";

string resolutioncodedtable = GetResolutionScopeTable(resolutionscope );

int resolutionrow = GetResolutionScopeValue(resolutionscope );

if ( resolutioncodedtable == "Module")

{

stringtypefminusnested = stringtypefminusnested + "/* 01" + k.ToString("X6") + " */";

}

if ( resolutioncodedtable == "AssemblyRef")

{

stringnested  = "[" + NameReserved(GetString(AssemblyRefStruct[resolutionrow].name)) + "/* 23" + resolutionrow.ToString("X6") + " */]";

stringtypefminusnested = stringtypefminusnested + "/* 01" + k.ToString("X6") + " */";

}

if ( resolutioncodedtable == "ModuleRef")

{

stringnested  = "[.module " + NameReserved(GetString(ModuleRefStruct[resolutionrow].name)) + "/* 1A" + resolutionrow.ToString("x6") + " */]";

stringtypefminusnested = stringtypefminusnested + "/* 01" + k.ToString("X6") + " */";

}

if ( resolutioncodedtable == "TypeRef" )

{

string resolutioncodedtable1 = GetResolutionScopeTable(TypeRefStruct[resolutionrow].resolutionscope );

int resolutionrow1 = GetResolutionScopeValue(TypeRefStruct[resolutionrow].resolutionscope );

if ( resolutioncodedtable1 == "AssemblyRef" || resolutioncodedtable1 == "ModuleRef" || resolutioncodedtable1 == "Module")

{

stringnested = GetTableRefNameForFillArray(resolutionrow1 , resolutionrow , resolutioncodedtable1);

stringnested = stringnested + GetTypeRefForFillarray(k);

stringtypefminusnested = "";

}

else

{

string tablename = GetResolutionScopeTable(TypeRefStruct[k].resolutionscope );

int row  = GetResolutionScopeValue(TypeRefStruct[k].resolutionscope );

int cnt = 0;

while ( tablename == "TypeRef")

{

row  = GetResolutionScopeValue(TypeRefStruct[row].resolutionscope );

tablename = GetResolutionScopeTable(TypeRefStruct[row].resolutionscope );

cnt++;

}

int i = 0;

int [] typerows = new int[cnt];

string tablename1 = GetResolutionScopeTable(TypeRefStruct[k].resolutionscope );

int row1  = GetResolutionScopeValue(TypeRefStruct[k].resolutionscope );

while ( i < cnt )

{

row1  = GetResolutionScopeValue(TypeRefStruct[row1].resolutionscope );

tablename1 = GetResolutionScopeTable(TypeRefStruct[row1].resolutionscope );

typerows[i] = row1;

i++;

}

 

int assemblyrow = GetResolutionScopeValue(TypeRefStruct[row].resolutionscope );

int typerow = typerows[typerows.Length-1];

stringnested = GetTableRefNameForFillArray( assemblyrow , typerow , tablename);

 

i = 0;

string dummy = "";

while ( i < cnt-1 )

{

dummy  =  GetTypeRefForFillarray(typerows[i]) + "/" + dummy;

i++;

}

stringnested = stringnested + dummy;

stringnested = stringnested + GetTypeRefForFillarray(resolutionrow);

stringnested = stringnested + "/" + GetTypeRefForFillarray(k);

stringtypefminusnested = "";

}

}

typerefnames[k] = stringnested  + stringtypefminusnested ;

//Console.WriteLine(".......{0} {1} {2}" , resolutioncodedtable , typerefnames[k] , k);

}

}

old = tableoffset;

tablehasrows  = tablepresent(2);

offs = tableoffset;

tableoffset = old;

if ( tablehasrows )

{

typedefnames = new string[rows[2]+1];

for ( int k = 1 ; k <= rows[2] ; k++)

{

int name = TypeDefStruct[k].name;

offs += offsetstring;

int nspace = TypeDefStruct[k].nspace;

offs += offsetstring;

string nestedtypestring  = "";

nestedtypestring  = GetNestedTypeAsString(k);

string namestring  = GetString(name);

string namespacestring = NameReserved(GetString(nspace));

if ( namespacestring.Length != 0)

namespacestring = namespacestring + ".";

namestring  = NameReserved(namestring );

typedefnames[k] = nestedtypestring + namespacestring + namestring  + "/* 02" + k.ToString("X6") + " */";

}

}

}

 

e.il

.assembly extern vijay

{

}

.module extern bbb

.module ccc

.class zzz

{

.method  class [vijay]z1 a1()

{

}

.method  class [.module bbb]z2 a2()

{

}

.method  class [.module ccc]z3 a3()

{

}

.method  class [vijay]z4/z5 a41()

{

}

.method  class [vijay]z4/z5/z6 a5()

{

}

.method  class [vijay]z4/z5/z6/z7 a6()

{

}

.method  class [vijay]z4/z5/z6/z7/z8/z9/z10/z11 a71()

{

}

.method  class [.module bbb]z4/z5/z6/z7/z8/z9/z10/z11 a72()

{

}

.method  class [.module ccc]z4/z5/z6/z7/z8/z9/z10/z11 a73()

{

}

}

 

Output

  .method /*06000001*/   class [vijay/* 23000001 */]z1/* 01000002 */

  .method /*06000002*/   class [.module bbb/* 1A000001 */]z2/* 01000003 */

  .method /*06000003*/   class z3/* 01000004 */

  .method /*06000004*/   class [vijay/* 23000001 */]z4/* 01000005 *//z5/* 01000006 */

  .method /*06000005*/   class [vijay/* 23000001 */]z4/* 01000005 *//z5/* 01000006 *//z6/* 01000007 */

  .method /*06000006*/   class [vijay/* 23000001 */]z4/* 01000005 *//z5/* 01000006 *//z6/* 01000007 *//z7/* 01000008 */

  .method /*06000007*/   class [vijay/* 23000001 */]z4/* 01000005 *//z5/* 01000006 *//z6/* 01000007 *//z7/* 01000008 *//z8/* 01000009 *//z9/* 0100000A *//z10/* 0100000B *//z11/* 0100000C */

  .method /*06000008*/   class [.module bbb/* 1A000001 */]z4/* 0100000D *//z5/* 0100000E *//z6/* 0100000F *//z7/* 01000010 *//z8/* 01000011 *//z9/* 01000012 *//z10/* 01000013 *//z11/* 01000014 */

  .method /*06000009*/   class z4/* 01000015 *//z5/* 01000016 *//z6/* 01000017 *//z7/* 01000018 *//z8/* 01000019 *//z9/* 0100001A *//z10/* 0100001B *//z11/* 0100001C */

 

 

 

So far we have looked at data types that have been created within our program and now we need to look at those defined somewhere else. Thus the method a1 has a return type of class z1. The only problem is that this class z1 is defined in vijay which is placed in square brackets.

 

This name vijay has to been a assembly ref as we specified earlier. The only problem is there is no check to see whether this assembly vijay actually exits and within it there exists a type called z1. Every type we reference in our program is stored in the type ref table.

 

The name and namespace field store for us the name and namespace name respectively. The field resolution we will deal with in  a short while. In the type signature, the class type remains at 12 and the token for the method a1 is 09. As the first bit is 1, this token refers to the type ref table and not the typedef table.

 

Thus in the DecodeToken table the if statement checks if the variable tabletype is 1, it picks up the row from the typeref and not typedef table. Thus in the FillArray method we now populate the typerefnames array. The variable stringtypefminusnested simply gives us the name and namespace combo of the type used with or without the dot.

 

Then we move on to the core of what this program is all about. We want to know where this type is really stored. The field resolutionscope is just what the doctor ordered. This method used earlier gives us one of the four tables, AssemblyRef, Module, ModuleRef and TypeRef. These values tell us where to find the type.

 

For the method a1, the type is to be found in a Assembly and hence the coded index is a AssembleyRef. The coded index row variable now points to the AssemblyRef table.

 

Thus we start with a open [, then we pick up the name of the assembly from the AssemblyRef table using the resolutionrow variable, followed by the table number of the AssemblyRef table 0x23 and the row number in this table using the resolutionrow variable. Finally the for loop index variable k tells us the row number in the type ref table that has a number 1.

 

This is at the end as a comment for all the typeref rows. We add the two strings stringtypefminusnested and string stringnested at the end and place this value into the typerefnames array. In this case we need not have two separate variables as one would have done the job. Read on.

 

For method a2, the return type is z2 and this is now found in a module bbb. There is a module extern directive that specifies the existence of a module with this name. Thus the coded index table is now ModuleRef as the type is in the same assembly but a different module.

 

For the module ref we add the .module directive and now the variable resolution row is an offset into the ModuleRef table whose number is 0x1a. The rest of the code remains like that of the AssemblyRef.

 

The third method a3 has its return data type z3 stored in a module called ccc and this is also the name of the current module using the module directive. Thus the code index table will return Module and in the output we do not display the words module and the name of the module, but only the type name.

 

The problems starts with the method a4 that has a type in the AssemblyRef vijay but its type is nested. The main type is z4 and within this type we have a nested type z5. The Nested Types table does not get an entry added as that table deals with nested types created in this assembly and not somewhere else.

 

Thus two records will be added to the TypeRef table, one for the type z4 and the second for the type z5. To make matters simpler for us to understand we will comment the entire il file and only focus on one method at a time from now on. Our current focus is method a4.

 

The TypeRef table has three records, the first for the type Object as all classes are derived from class Object at the end of the day. Thus we will not show you the first record of the type ref table that refers to the class Object.

 

The second row refers to the class z4 and the resolution scope refers to the assembly ref table record 1 that is vijay. This is obvious as the class or type must reside someplace. The third record refers to the class z5. This class is nested within the class z4.

 

This relationship is mapped by specifying the resolution scope of TypeRef and the row number is the row number of the class within which this class is nested. Thus whenever we see a TypeRef resolution scope it means that the current class  is nested within the class specified by the resolution index.

 

This class if it has a resolution scope of TypeRef means one more level of nesting. This relationship ends with a type whose resolution scope is Module, ModuleRef or AssemblyRef as we shall soon see.

 

Row #2

ResolutionScope   : AssemblyRef[1]

Name              : "z4"

Row #3

ResolutionScope   : TypeRef[2]

Name              : "z5"

 

The if statement for the value of the resolution scope index being TypeRef is true for the nested type z5 and not type z4. The value of the variable resolutionrow is now 2 which simply means that this TypeRef row may be the parent type.

 

We use this value to move to row 2, we are at row three now and find out the new values of this coded index. If it is any of the terminating table types, we call the function GetTableRefNameForFillArray that gives us the name of the Assembly or Module or ModuleRef that the type is in.

 

The last parameter is the type of container that this type resides in. The first parameter is the tablerow in the table type specified in the last parameter and the second parameter is the type row number that has a coded index other than  TypeRef. Lets look at this function GetTableRefNameForFillArray first.

 

We first ask which table the tablerow parameter is an offset to. Depending upon one of two and not three possible tables, we use the relevant array to gives us the name of the assembly or module. If the type resides in the same module, the module directive is removed.

 

For the AssemblyRef table, it is simply the table name and for the ModuleRef table it is the module directive plus the extern module name. We now need to add the type name and namespace name which is stored in the TypeRef table. This type is the parent type z4 and not nested type z5 in this case.

 

We then add the relevant name of the type and the row number in comments. As this is a nested type we end with a single slash specifying a nested type. Now that we have the parent type, we need to add the last nested type z5 which is the value of the loop index k.

 

Thus we have a method GetTypeRefForFillarray that simply displays a type with a certain index. This function is very elementary and all that we do is add the name and namespace and then the row number of the type ref table in comments. This is how we take care of a single level of a nested type.

 

This does not work with a type that has more than one level of nesting. We need more complex code. This if statement was unnecessary but as an aid to understanding. The else takes place whenever we have more than one level of nesting. Lets take a certain case of method a6 which is complex enough.

 

Its data type of the return value is z4/z5/z6/z7 where we have four levels of nesting. How do we handle complex cases like this. Simple we start at the inner else statement.

 

Row #2

ResolutionScope   : AssemblyRef[1]

Name              : "z4"

Row #3

ResolutionScope   : TypeRef[2]

Name              : "z5"

Row #4

ResolutionScope   : TypeRef[3]

Name              : "z6"

Row #5

ResolutionScope   : TypeRef[4]

Name              : "z7"

 

Lets start with the rows of the TypeRef table. In the DecodeToken method, the value of the tableindex will b3 5, the record number for the z7 type. If we look at the rows, the resolution coded table is typeref that tells us that the parent type of z7 is z6.

 

But this row also has a coded index of TypeRef and thus class z5 is the parent of class z6. However class z5 or row 3 has also a TypeRef coded index and thus row 2 or class z4 is the parent for class z5. The good news is that class z4 has a coded index of AssemblyRef. Lets take a hands on look at our code.

 

The string tablename is TypeRef and the type row is 5 or class z7. The row variable is 4 specifying that this row will be the parent of class z7. We would now need to find out how many levels of nesting are there. Thus we need to keep looping until we find a type row whose coded index table is not TypeRef.

 

The problem is that we cannot assume that each nested types parent is stored one about it. So we in a while loop, keep looping until we reach a table name that is not TypeRef. Now that we have entered the while loop, we are on a nested type and hence we calculate the row number of the parent type.

 

This is stored in row. We then use this row number to give us the coded index table of the parent type. If this is ever not TypeRef we exit the while loop. Thus in our specific case the cnt variable will have a value of 2 as we have entered the while loop twice and the row number that had a coded index other than TypeRef is the second row.

 

Thus the value of cnt is actually one less than the number of types. It gives us types z5 and z6. Z7 is the type denoted by the k index and z4 by the AssembleyRef. We would now like to create an array of 2 int’s t store the row numbers of types z5 and z6.

 

We once again enter the while loop twice and simply recalculate the coded index row variable and store it in the typerows array. The values in this array are rows 3 and  2 as we are moving up the type row table. The tablename1 and row1 variables are set to a value of the type z7 or row 5 in the type ref table.

 

We are now calling the method GetTableRefNameForFillArray with the first parameter assemblyrow as 1 as this is the record number of the AssemblyStruct table and name vijay. The typerow variable is the 2 as this is the first parent type z4. This method will return a string as [vijay/* 23000001 */]z4/* 01000002 *//.

 

We now enter the while loop only once as the value of cnt is only two. Here we display the next series of nested types. The typerow array has a value of 3 and the string dummy contains z5/* 01000003 *//. Now we need to display the second last type that is denoted by the resolutionrow variable or row 4 or type z6.

 

Finally the current type denoted by k or row 5 or type z7 is displayed at the end without the nested slash. We could not have placed the second last GetTypeRefForFillarray within the while as the type z4 would come twice. If you are not clear please reread the entire explanation a couple of times with some aspirin.

 

Program32.csc

public string GetElementType ( int index , byte [] blobarray , out int howmanybytes)

{

howmanybytes = 0;

string returnstring = "";

string modoptstring = "";

int bytestaken;

modoptstring = GetModOptOrModReq( index , blobarray , out bytestaken);

index = index + bytestaken;

byte type = blobarray[index];

if ( type >= 0x01 && type <= 0x0e )

{

returnstring = GetType(type);

howmanybytes = 1;

}

if ( type == 0x45 )

{

int howmanybytes2 ;

returnstring = GetElementType( index + 1 , blobarray , out howmanybytes2) + " pinned";

howmanybytes = howmanybytes2 + 1;

}

howmanybytes = howmanybytes + bytestaken;

if ( modoptstring != "")

returnstring = returnstring + " " + modoptstring;

return returnstring ;

}

public string GetTokenType ( byte [] blobarray , int index , out int howmanybytes)

{

string returnstring = "";

int uncompressedbyte;

int howmanybytes1 = 0;

howmanybytes1 = howmanybytes1  + CorSigUncompressData(blobarray , index + 1 , out uncompressedbyte);

string dummy1  = DecodeToken(uncompressedbyte , blobarray[index]);

if ( blobarray[index] == 0x12)

returnstring = "class " + dummy1; 

else if ( blobarray[index] == 0x11)

returnstring = "valuetype " + dummy1;

else

returnstring = dummy1;

howmanybytes = howmanybytes1;

return returnstring;

}

 

public string GetModOptOrModReq ( int index , byte [] blobarray , out int bytestaken)

{

string returnstring = "";

bytestaken = 0;

int whileindex = index;

if ( blobarray[index]  == 0x20 || blobarray[index]  == 0x1f)

{

while ( true )

{

int noofbytes;

string tokens = GetTokenType (blobarray , whileindex , out noofbytes) ;

if ( blobarray[whileindex]  == 0x20)

tokens = "modopt(" + tokens + ")";

if ( blobarray[whileindex]  == 0x1f)

tokens = "modreq(" + tokens + ")";

bytestaken = bytestaken + noofbytes + 1;

returnstring = tokens + " " + returnstring ;

whileindex = whileindex + noofbytes + 1;

if ( !(blobarray[whileindex]  == 0x20 || blobarray[whileindex]  == 0x1f)  )

break;

}

}

if ( returnstring != "")

returnstring = returnstring.Remove(returnstring.Length-1,1);

return returnstring;

}

 

e.il

.assembly extern vijay

{

}

.class zzz

{

.method  void modopt([vijay]aa  ) a1()

{

}

.method  void modreq([vijay]aa ) a2()

{

}

.method  void * modopt([vijay]aa ) modreq([vijay]bb ) modreq ([vijay]cc )  modreq([vijay]dd )  a3()

{

}

}

 

Output

  .method /*06000001*/   void modopt([vijay/* 23000001 */]aa/* 01000002 */)

  .method /*06000002*/   void modreq([vijay/* 23000001 */]aa/* 01000002 */)

  .method /*06000003*/   void* modopt([vijay/* 23000001 */]aa/* 01000002 */) modreq([vijay/* 23000001 */]bb/* 01000003 */) modreq([vijay/* 23000001 */]cc/* 01000004 */) modreq([vijay/* 23000001 */]dd/* 01000005 */)

 

We are now going to delve into two modifiers modreq and modopt that we have no parallel when it comes to the C# programming language. If you were a C++ programmer, thee custom modifiers may have made some sense. These modifiers are similar to custom attributes but they are part of the method signature unlike attributes that are not.

 

The modreq modifer means that the caller must understand the modifer whereas modopt means that the modifer may be ignored. These modifiers can be attached to a parameter or a return value, in other words they associate only with a data type. This allows us to associate a type reference with a item.

 

The CLI however does not care about these modifiers other than changing the signature of the method. These modifiers are thus used by tools that interact with the metadata. These tools are compilers, program analysers etc. These modifiers tell us that a modopt may be ignored but a modreq has some special meaning.

 

If this was a C++ book, we would have explained how the concept of a const can be modelled using custom modifiers. Lets look at the first method a1 which has a optional modifer with a type reference to type aa in assembly vijay. The modifiers need a any type reference. This could be a type created somewhere else or in the same file.

 

A int32 type will not suffice. For consistency we have used a type aa for all. The signature for this method reads as 20 00 20 09 01. You will realize that the third byte is not a 1 which is the data type of a void but 0x20. This is the type number for a modopt modifier.

 

These modifiers are followed by a token type that pinpoints the type reference. This is the reason in built types like int32 are not permitted. After the type token follows the data type of the return value. Lets look at method a2 that uses a modreq instead.

 

Its signature is 20 00 1F 09 01 which tells us that a modreq has a type of 0x1f. Finally method a3 is more complex, it starts with one modopt and then we have three modreq modifiers. Its signature is 20 00 1F 15 1F 11 1F 0D 20 09 0F 01. The last two bytes are for the pointer to void.

 

Even though we start with a modopt, then the modreqs, the order is the signature is reversed. This we see the three modreqs first and then the modopt. When we display these custom modifiers we will have to reverse them. Lets start with the method GetElementType first.

 

At the very beginning we would like to find out if there are any custom modifiers. This is most important as custom modifiers come first and then the data type. This is true for a return value as well as for parameters.

 

We pass the function GetModOptOrModReq three parameters, the index of the type in the blob array, the blobarray and we expect a string that has all the modopt and modreq modifiers. Also we expect the out parameter to give us the number of bytes taken up by the custom modifiers.

 

We then increase the index variable by the number of bytes taken up by the custom modifiers so we now point to the actual data type. At the end of the GetElementType method we add the howmanybytes variable by the bytestaken or the number of bytes taken up by the custom modifiers.

 

This variable is the out parameter for the GetElementType method. Finally we add the modoptstring to the end of the returnstring and not at the beginning with a space. Lets move on the GetModOptOrModReq that actually does all the work. Most of the time, custom modifiers are rarely used and this method will do nothing.

 

Thus we initialize the out parameter bytestaken to b zero and the returnstring to a empty string. If and only if the byte denoted by the index parameter is a modreq 0x1f or a modopt  0x20, do we need to do something. As we may have scores of custom modifiers we enter a infinite while loop.

 

The fact that we are here now means that we have at least one custom modifer. We then call the  method GetTokenType to figure out the token that follows a custom modifer. This method remains the same like before but with a small else added.

 

Earlier we called this function for a class or valuetype token and within if statements added a class or valuetype to the value returned by the DecodeToken method. Now as we are calling it from a third method, we need to add no such embellishments to the token string and thus the final else simply initializes the return value.

 

Thus variable tokens now contains the type reference token as a string but we need to place this token in a modopt or modreq keyword. Thus we check whether the byte type is that of modopt 0x20 or modreq 0x1f. We use the variable whileindex for this offset.

 

We now set bytestaken to increase by the number of bytes the token has taken that is stored in noofbytes and again by 1 as one byte gets taken by the custom modifier. We then set the return string returnstring to this value of the token but as the world is reverse, we add the tokens at the beginning and not at the end.

 

We increase the whileindex by the same amount as bytestaken and now check if the next byte after the custom modifier is also another custom modifer. If it is we go back to the loop, if not we end the loop. Finally these is an extra space left in this string returnstring which we remove using the Remove method.

 

This is how we handle custom modifiers.

 

Program33.csc

public void CreateMethodDefSignature (byte [] blobarray , int row)

{

string returntypestring = "";

returntypestring = GetElementType(index  , blobarray , out howmanybytes ,row , "Method") + " ";

methoddefreturnarray[row] = returntypestring;

}

public void DisplayAllMethods (int typerow)

{

methodstring = methodstring  + ".method ";

methodstring = methodstring + "/*06" + methodindex.ToString("X6") + "*/ " ;

string s = methodstring + "  " + methoddefreturnarray[methodindex] + " " + GetString(MethodStruct[methodindex].name);

Console.WriteLine(s);

}

public string GetElementType ( int index , byte [] blobarray , out int howmanybytes , int row , string name)

{

howmanybytes = 0;

string returnstring = "";

string modoptstring = "";

int bytestaken;

modoptstring = GetModOptOrModReq ( index , blobarray , out bytestaken);

index = index + bytestaken;

byte type = blobarray[index];

else if ( type == 0x1b)

{

int howmanybytes2 ;

returnstring = GetPointerToFunctionSignature ( index , blobarray , out howmanybytes2 , ref modoptstring, row , name) ;

modoptstring = "";

howmanybytes = howmanybytes2 ;

}

howmanybytes = howmanybytes + bytestaken;

if ( modoptstring != "")

returnstring = returnstring + " " + modoptstring;

return returnstring ;

}

public string GetPointerToFunctionSignature ( int index , byte [] blobarray , out int howmanybytes , ref string modoptstring, int row , string name)

{

int uncompressedbyte;

index = index + 1;

int nobytes ;

int totalbytes = 1;

string returnstring = "";

nobytes = CorSigUncompressData(blobarray , index , out uncompressedbyte);

if ( row != 0 && name == "Method")

{

methoddeftypearray [row] = DecodeFirstByteofMethodSignature (uncompressedbyte , row);

methoddeftypearray[row] = "method " + methoddeftypearray [row];

}

else

returnstring =  "method " + DecodeFirstByteofMethodSignature(uncompressedbyte , row);

index = index + nobytes;

totalbytes = totalbytes + nobytes;

nobytes = CorSigUncompressData(blobarray , index , out uncompressedbyte);

index = index + nobytes;

totalbytes = totalbytes + nobytes;

int nooftypes = uncompressedbyte;

int bytestaken1;

string modoptstring1 = GetModOptOrModReq( index , blobarray , out bytestaken1);

index = index + bytestaken1;

totalbytes = totalbytes + bytestaken1;

returnstring = returnstring + GetElementType(index  , blobarray , out nobytes , 0 , "") ;

index = index + nobytes;

totalbytes = totalbytes + nobytes;

if (modoptstring1!= "")

returnstring = returnstring + " " + modoptstring1 ;

returnstring = returnstring + " *(";

if ( nooftypes == 0)

returnstring = returnstring + ")";

else

for ( int i = 1; i <= nooftypes ; i++)

{

returnstring = returnstring + GetElementType(index  , blobarray , out nobytes ,0 , "");

if ( i != nooftypes)

returnstring = returnstring + ",";

else

returnstring = returnstring + ")";

index = index + nobytes;

totalbytes = totalbytes + nobytes;

}

if ( modoptstring != "")

returnstring = returnstring + " " + modoptstring;

howmanybytes = totalbytes;

return returnstring;

}

 

e.il

.assembly extern vijay

{

}

.class zzz

{

.method public static method unmanaged stdcall int32*(int8,int16,string ) a1(unsigned int8  a)

{

}

.method public static method unmanaged fastcall int32  modopt(zzz) modopt ([vijay] yyy) modopt ([vijay] xxx)  *(int8,int16,string ) modreq([vijay]a1) modreq([vijay]a2) a2(int8  a, int16 b)

{

}

.method public static method unmanaged thiscall int32  *(int8,int16,string ) modreq([vijay]a1) modreq([vijay]a2) a3(int8  a, int16 b)

{

}

.method public static method unmanaged cdecl int32  modopt(zzz) modopt ([vijay] yyy) modopt ([vijay] xxx)  *(int8,int16,string ) a4(int8  a, int16 b)

{

}

}

 

Output

 

  .method /*06000001*/   int32 *(int8,int16,string)  a1

  .method /*06000002*/   int32 modopt(zzz/* 02000002 */) modopt([vijay/* 23000001 */]yyy/* 01000002 */) modopt([vijay/* 23000001 */]xxx/* 01000003 */) *(int8,int16,string) modreq([vijay/* 23000001 */]a1/* 01000004 */) modreq([vijay/* 23000001 */]a2/* 01000005 */)  a2

  .method /*06000003*/   int32 *(int8,int16,string) modreq([vijay/* 23000001 */]a1/* 01000004 */) modreq([vijay/* 23000001 */]a2/* 01000005 */)  a3

  .method /*06000004*/   int32 modopt(zzz/* 02000002 */) modopt([vijay/* 23000001 */]yyy/* 01000002 */) modopt([vijay/* 23000001 */]xxx/* 01000003 */) *(int8,int16,string)  a4

 

In the above example the DisplayAllMethods function has only one change and that is the name of the method stored in the field name is also displayed. The major changes takes place in the GetElementType method. We have added two more parameters to this method. This is how code gets written in real life.

 

As we did no prior planning earlier, we did not account for the fact that the existing parameters for the GetElementType method we not enough. Thus each and every place we have called the GetElementType method in the past, we now have to add these two extra parameters.

 

As these earlier calls did not use these parameters, we use 0 and the empty string “”. The only place we use it is in the CreateMethodDefSignature method where we pass the row number in the method table for the fourth  parameter and the fifth is where we have called the GetElementType method from, the Method table obviously.

 

There is one more addition to the GetElementType method and that is the type 0x1b that stands for a full method signature. Lets look at the first method a1 in the il file. The first change is the addition of the word method to the calling convention. This is followed by the data type int32 and then a star.

 

This complicates life as we now have a pointer to a function. The open and close brackets now encompass the parameters of this function. Whenever we have a pointer to a function, we shall see a 0x1b in our signature byte. These bytes now look as 00 01 1B 02 03 08 04 06 0E 05.

 

The first is the calling convention byte that is zero, followed by the number of parameters of the method a1 01. Then we get a 0x1b which is the type for a full method signature following. This byte now has the calling convention byte first and 2 is stdcall. As this is a pointer to a function, we need to add the word method to the calling convention.

 

The next byte is the stdcall calling convention followed by not the count bytes of number of parameters to function a1 but to the function that the return value points to 3. Each and every function call returns a value and in our case our function returns a int32 and its type byte 08 is next. This is followed by the three parameter types to the method a int8 or 04, then a int16 a 06 and a string 0e.

 

This completes the pointer to method and then we have the data type of the first parameter. Method a2 complicates life a little bit for us. The modopt and modreq appear after any data type. We have two data types in a pointer to function situation, the first is the int32 or return type of the pointer method.

 

In function a2 we have a series of modopt which if you forgotten is 0x20 and then we have a series of modreqs or 0x1f after the pointer function parameters and before the name of the function. Thus the signature bytes read as 00 02 1F 15 1F 11 1B 04 03 20 0D 20 09 20 08 08 04 06 0E 04 06.

 

This make look complex but if you look at them closely, you will see that byte 3 onwards are the modreqs 0x1f. Thus what we write at the end come first in the signature. This is then followed by the signature of the pointer to function 0x1b and the next two bytes.

 

Then come the modopt’s and return value of the pointer, the three function pointer data types and the data types of the three parameters. Methods a3 and a4 have the required modifer at the beginning or at the end. First lets look at the code of the GetElementType method All that we do is what we have always done.

 

We call a method GetPointerToFunctionSignature which takes the same five parameters passed to the GetElementType method. The fourth is a valid row number of the method in the method table and the last is the string Method. In the same vein the howmanybytes variable is set to the third parameter of the GetPointerToFunctionSignature method.

 

The major deviation is that we set the variable modoptstring to null which means that we undo the effect of the first GetModOptOrModReq method called. Also we pass this string modoptstring  as the fifth parameter. Coming to the GetPointerToFunctionSignature method we start with using the index variable as an offset into the signature.

 

We are now at the 0x1b and need to pick up the calling convention byte. We increase the index variable by 1 and also use the totalbytes to tell us how many bytes the pointer to function signature consumes. As the 0x1b takes up one byte, we start variable totalbytes with 1 and not zero.

 

Now we need to decode the calling convention byte using the method DecodeFirstByteofMethodSignature that we have done before. We also need to add the words method before it. The only problem is that for a method we have stored the calling convention in the array methoddeftypearray.

 

For others like a local var signature, method ref signature etc we have created no such array. Thus we check if the row is non zero and the name parameter is Method. If yes we simply place the calling convention string in the array  methoddeftypearray.

 

If another signature, we add the calling convention to the returnstring variable that will contain the entire signature to be returned. The index and totalbytes variable will move in lock step always. We then take care of the number of parameters to the function by storing the result in nooftypes that we use later to simply print out the data types to the method.

 

Now we may or may not have custom modifiers modopt or modreq. Here the modifiers are the one before and the first set of modifiers are stored in the string modoptstring. We then call the GetElementType method to gives the data type of the pointer to function which is a int32 in our case.

 

We once increase the index and totalbytes variables. We now check whether the modoptstring1is blank or not as we need to add the modopt string with a space. These custom modifiers are the custom modifiers that we have just got. We then place the * and the brackets and if the function has no data types, i.e. nooftypes is zero we add the closing brackets.

 

We then in a for loop use GetElementType to give us the data type of each parameter. We also know that of the last parameter we do not put a comma but a close bracket. Finally we add the last modoptstring that actually comes first in the signature byte and we captured at the beginning of the GetElementType method.

 

We also initialize the out parameter howmanybytes to totalbytes. This completes what we believe is the most difficult method signature.

 

Finally we have used the same pointer to function as a parameter. .method public static int32 a2(method unmanaged stdcall int8 modopt([vijay]aa) modopt([vijay]bb) * (int16,int32) modreq([vijay]cc) a )

{

}

 

Its signatures bytes are 00 01 08 1F 11 1B 02 02 20 0D 20 09 04 06 08. Thus life remains the same whether we deal with parameters or return values.

 

We finally take a real complex method signature. First lets take a look at the il file.

 

e.il

.assembly extern vijay

{

}

.class zzz

{

.method public static method unmanaged stdcall bool modopt([vijay]bb) * ( method unmanaged thiscall  int8 modreq([vijay]cc) modreq([vijay]dd) *(int16,method unmanaged fastcall int32 modreq([vijay]z1) modopt([vijay]z2) * ( bool , unsigned int32) ) , int64 , unsigned int64) modreq([vijay]ee) a2()

{

}

}

 

Looks very intimidating but lets take it one byte at a time by looking at the signature generated. This looks like 00 00 1F 1D 1B 02 03 20 09 02 1B 03 02 1F 11 1F 0D 04 06 1B 04 02 20 19 1F 15 08 02 09 0A 0B.Lets as always start from the beginning taking.

 

The first 00 is the calling convention and the second 00 the number of parameters the function a1 takes that are none. Then we come across the modreq modifer 1f that is the last modreq modifer modreq([vijay]ee). Remember these modifiers are stored backwards, the last is seen first.

 

Then we have a 0x1b or the pointer to function signature. The 02 is the standard calling type stdcall of the main pointer to function that is followed by the number of parameters 03 that this pointer has. The last two are a int64 and unsigned int64. This is followed by the two modopt’ s modopt([vijay]aa) modopt([vijay]bb)  that we have before the *.

 

The 02 is the data type for a bool which is the return value data type for the pointer to function. Then we have the data type of the first parameter out of three which unfortunately starts with a 0x1b or a pointer to function. The 03 is the data type fir a thiscall and the 02 is the number of parameters of this pointer to function.

 

The two 0x1f’s are the modreq’s modreq([vijay]cc) modreq([vijay]dd) and the 04 is the data type int8 the return type of this pointer to function. As we have two parameters to this function a int16 whose data type is 06. The second parameter is a another pointer to function and hence we see a 0x1b.

 

Its calling convention is 04 or fastcall. The next 02 tells us that we have two parameters. We have a modopt and a modreq following modreq([vijay]z1) modopt([vijay]z2). The int32 data  is the return type of the function and this accounts for the 08. The 02 and 09 and the bool and unsigned int32 data types of the innermost pointer to function.

 

Finally the in64 and unsigned int64 have a data type 0f 0x0a and 0x0b that take up the last two bytes of the signature.

 

The following il file hangs ilasm.

 

.method public static method unmanaged stdcall bool modopt([vijay]bb) * ( method unmanaged thiscall  int8 modreq([vijay]cc) modreq([vijay]dd) *(int16,method unmanaged fastcall int32 modreq([vijay]z1) modopt([vijay]z2) * ( bool , unsigned int32) ) ) modreq([vijay]ee) a2(int8 aa, int16 bb)

{

}

 

Program34.csc.txt

public void DisplayAllMethods (int typerow)

{

if ( TypeDefStruct == null)

return;

if ( MethodStruct == null)

return;

int start , startofnext=0;

start =  TypeDefStruct[typerow].mindex ;

if ( typerow == (TypeDefStruct.Length -1) )

{

startofnext= MethodStruct.Length;

}

else

startofnext = TypeDefStruct[typerow+1].mindex ;

for ( int methodindex = start ; methodindex < startofnext ; methodindex++)

{

string methodstring = CreateSpaces(spacesforrest);

if ( IsTypeNested(typerow))

methodstring = methodstring + CreateSpaces(spacesfornested);

methodstring = methodstring  + ".method ";

methodstring = methodstring + "/*06" + methodindex.ToString("X6") + "*/ " ;

string s = methodstring + methoddefreturnarray[methodindex] + GetString(MethodStruct[methodindex].name) + "(" + methoddefparamarray [methodindex] + ")" ;

Console.WriteLine(s);

}

}

string [] paramnames;

public void CreateMethodDefSignature (byte [] blobarray , int row)

{

//Console.WriteLine("CreateMethodDefSignature Array Length={0} method row={1} name={2}" , blobarray.Length , row , GetString(MethodStruct[row].name));

int howmanybytes,uncompressedbyte , count , index;

index = 0;

howmanybytes = CorSigUncompressData(blobarray , index , out uncompressedbyte);

methoddeftypearray[row] = DecodeFirstByteofMethodSignature(uncompressedbyte , row);

index = index + howmanybytes;

howmanybytes = CorSigUncompressData(blobarray , index , out uncompressedbyte);

count = uncompressedbyte;

methoddefparamcount[row] = count;

index = index + howmanybytes;

string returntypestring = "";

returntypestring = GetElementType(index  , blobarray , out howmanybytes ,row , "Method") + " ";

index = index + howmanybytes;

methoddefreturnarray[row] = returntypestring;

string returnstring1 = "" ;

string returnstring2 = "";

string typestring = "";

for ( int l = 1 ; l <= count ; l++)

{

typestring = GetElementType ( index , blobarray , out howmanybytes , 0 , "") ;

index = index + howmanybytes;

returnstring2= returnstring2 + typestring ;

if ( l != count)

returnstring2 = returnstring2 + ",";

int ind = GetParamRowNumber (row, l );

Now every parameter like a return value can have attributes like in, out, opt as well as marshalling information. The GetParamAttrforParamCalling function takes the param row number and returns the attributes.

 

string paramcalling = GetParamAttrforParamCalling (ind);

string parammarshal = GetParamAttrforParamMarshal(ind , 1 ) ;

returnstring1 = returnstring1 + paramcalling ;

returnstring1 = returnstring1 + typestring + " " ;

returnstring1 = returnstring1 + parammarshal ;

returnstring1 = returnstring1 + GetParamNameForMethod( row , count , l);

if ( l != count)

returnstring1 = returnstring1 + "," ;

methoddefparamarray[row] = returnstring1;

methoddefparamarray1[row] = returnstring2;

}

}

public string GetParamNameForMethod (int row , int count , int l)

{

string returnstring = "";

string mattributes = GetMethodAttribute(MethodStruct[row].flags , row);

if ( ParamStruct == null)

{

//isnan.exe

if ( mattributes.IndexOf("static") != -1)

l--;

return  "A_" + l.ToString();

}

int start , startofnext=0 , paramcount;

start =  MethodStruct[row].param ;

if ( row  == (MethodStruct.Length -1) )

startofnext= ParamStruct.Length;

else

startofnext = MethodStruct[row+1].param;

paramcount =   startofnext - start;

int ind;

bool paramfound = false;

for ( ind = start ; ind < startofnext ; ind++)

{

if ( ParamStruct[ind].sequence == l)

{

paramfound = true;

break;

}

}

if ( paramfound && paramnames[ind] != "" )

returnstring = NameReserved(paramnames[ind]);

else

{

//Interop.orders.dll

if ( mattributes.IndexOf("static") != -1)

l--;

returnstring = "A_" + l.ToString();

}

return returnstring;

}

int GetParamRowNumber( int row , int l )

{

if ( MethodStruct == null ||  ParamStruct == null)

return 0;

int start =  MethodStruct[row].param ;

int end = 0;

if ( row  == (MethodStruct.Length -1) )

end = ParamStruct.Length;

else

end = MethodStruct[row+1].param;

end--;

int i;

for ( i = start ; i <= end ; i++)

{

if ( ParamStruct[i].sequence == l)

return i;

}

return 0;

}

public string GetParamAttrforParamMarshal (int paramindex , int tabletype)

{

string returnstring = "";

if (ParamStruct == null  )

return "";

if (paramindex >= ParamStruct.Length )

return "";

int pattr = ParamStruct[paramindex].pattr;

returnstring = DecodeParamAttributes( pattr , tabletype , paramindex , 0x2000);

return returnstring;

}

public string GetParamAttrforParamCalling (int paramindex)

{

string returnstring = "";

if (ParamStruct == null  )

return "";

if (paramindex >= ParamStruct.Length )

return "";

if (ParamStruct[paramindex].sequence == 0)

return returnstring;

int pattr = ParamStruct[paramindex].pattr;

if ( (pattr & 0x01) == 0x01)

returnstring = returnstring + "[in]" ;

if ( (pattr & 0x02) == 0x02)

returnstring = returnstring + "[out]" ;

if ( (pattr & 0x10) == 0x10)

returnstring = returnstring + "[opt]" ;

if ( returnstring != "")

returnstring = returnstring + " ";

return returnstring ;

}

public void FillArray ()

{

int old = tableoffset;

bool tablehasrows = tablepresent(8);

int offs = tableoffset;

tableoffset = old;

if ( tablehasrows )

{

paramnames = new string[rows[8] + 1];

for ( int k = 1 ; k <= rows[8] ; k++)

{

short pattr = BitConverter.ToInt16 (metadata, offs);

offs += 2;

int sequence = BitConverter.ToInt16 (metadata, offs);

offs += 2;

int name = ReadStringIndex(metadata, offs);

offs += offsetstring;

string dummy = GetString(name);

paramnames [k] = NameReserved(dummy);

}

}

}

 

 

e.il

.assembly extern vijay

{

}

.class zzz

{

.method void a1(int8 i1 , int16 i2)

{

}

.method void a2(int8  , int16 i3)

{

}

.method static void a21(int8  , int16 i3)

{

}

.method static void a3(int8  , int16 )

{

}

.method [in] void a4( int8 i4 , int16 i5)

{

}

.method  void marshal (int32) a5 ([out] int8 i4 , int16 i5)

{

}

.method void  modopt([vijay]aa) modreq([vijay]bb) a6([out] int8 i4 , int16 i5)

{

}

.method public static method unmanaged stdcall int32*(int8,int16 aa ,string bb) a7(unsigned int8  a)

{

}

//.method public static method unmanaged stdcall bool *( method unmanaged thiscall  int8 *(int16,method unmanaged fastcall int32 * ( bool pp, unsigned int32 kk) ) , int64 gg, unsigned int64 dd) a82(){}

}

 

Method name=a1 ParmNo=1

Method name=a2 ParmNo=3

Method name=a21 ParmNo=5

Method name=a3 ParmNo=7

Method name=a4 ParmNo=9

Method name=a5 ParmNo=12

Method name=a6 ParmNo=15

Method name=a7 ParmNo=17

Param 1 name=i1 seq=1 attr=0

Param 2 name=i2 seq=2 attr=0

Param 3 name=A_0 seq=1 attr=0

Param 4 name=i3 seq=2 attr=0

Param 5 name=A_0 seq=1 attr=0

Param 6 name=i3 seq=2 attr=0

Param 7 name=A_0 seq=1 attr=0

Param 8 name=A_1 seq=2 attr=0

Param 9 name= seq=0 attr=1

Param 10 name=i4 seq=1 attr=0

Param 11 name=i5 seq=2 attr=0

Param 12 name= seq=0 attr=8192

Param 13 name=i4 seq=1 attr=2

Param 14 name=i5 seq=2 attr=0

Param 15 name=i4 seq=1 attr=2

Param 16 name=i5 seq=2 attr=0

Param 17 name=a seq=1 attr=0

 

Output

  .method /*06000001*/ void a1(int8 i1,int16 i2)

  .method /*06000002*/ void a2(int8 A_0,int16 i3)

  .method /*06000003*/ void a21(int8 A_0,int16 i3)

  .method /*06000004*/ void a3(int8 A_0,int16 A_1)

  .method /*06000005*/ void a4(int8 i4,int16 i5)

  .method /*06000006*/ void a5([out] int8 i4,int16 i5)

  .method /*06000007*/ void modopt([vijay/* 23000001 */]aa/* 01000002 */) modreq([vijay/* 23000001 */]bb/* 01000003 */) a6([out] int8 i4,int16 i5)

  .method /*06000008*/ int32 *(int8,int16,string) a7(unsigned int8 a)

 

In the DisplayAllMethods method we now print out the contents of the array methoddefparamarray. In this example we will display the method along with all the parameter data types and names. Before we move onto the code, lets look at the il file first and at the same time we have displayed the rows from the method table as well as the Param table.

 

Lets look at method a1 in the il file, it has two parameters i1 and i2. The method table has a field called Param that behaves like the method field in the TypeDef table. The current row tells us the first row number in the Param table and the next record tells us the first row of the next method in the Param table.

 

Thus for row 1 in the method table the param field has a value of 1 and for the next row the same field has a value of 3 as the first method has two params. Moving on to the Params table, the name field is the name of the param that is i1 and i2. In the second method a2, the first param has no name and the second param a name i1.

 

In il we are allowed to specify a parameter but give it no name. In spite of this, ilasm adds two records to the param table and lets the second param have its name i3, but gives the first param a name A_0. In method a21, the keyword static has been added and we yet have the unknown name parameter as A_0.

 

Thus adding static does not change the name that ilasm automatically chooses. In method a3, both the parameters have no names and the system decides the names A_0 and A_1 respectively. This creates one more parameter that has no name and a sequence number of zero.

 

In method a5 we have added the marshal keyword which also add a new parameter with no name but a sequence number of zero. In method a6 we have the modifiers modopt and modreq but this does not effect the number of rows in the param table unlike the earlier two cases.

 

Method a7 has a return value of a pointer to function but this does not matter to ilasm as the function called may have parameters but we do not specify any names. If we do, they get ignored. Now lets take a look at the CreateMethodDefSignature method that fills up the array methoddefparamarray with all the parameter data types and names.

 

The count variable tells us the number of parameters that we have and we iterate all the parameters using a for loop. We store in typestring the parameter data type that we get using the GetElementType method. We do not specify the method row number and a method name as we do not want to add the words method to the calling convention.

 

The returnstring2 variable is set to the value of typestring and then we decide to add a comma or not. The comma should be added to separate all parameters but the last. We now that we are on the last parameter when variable count is equal to the variable l.

 

Hey where are the parameter names. We need two types of signatures, one with parameter names when we display a declaration of a function. When we call a function we also need the signature but without the parameter names and only the data types. Thus we have two separate strings returnstring2 and returnstring1.

 

The function GetParamRowNumber is passed two parameters, the method row number and the parameter number l. Given these two values, it must return the corresponding row number owned by this parameter. If for some reason, a parameter does not own any row, this function returns zero.

 

Lets figure out how this function works. We first store in the start and end variable the first and last parameter row owned by this method. We reduce the end variable by 1 as we do not, it will give us the starting row of the next method. Then we iterate in a for loop starting from start and ending with end.

 

This means that if our method owns 5 rows in the param table, this loop will iterate five times. All that we check is for a record with the same sequence number that the parameter has. If we meet a match we return the param row number stored in the loop variable i.

 

If we get out of the loop, it means that this param have no row in the param table and hence we return 0. Earlier we showed you that in spite of having parameters with no names and attributes, the system yet allocated a row in the param table and gave the parameter a name. This is what the current verison of ilasm does.

 

Thus all that we need to do is figure out the starting param row number and then add l to it. The only precaution we need to take is ask whether we have a param number with a sequence of zero. The point is that today every parameter owns one record in the param table.

 

Earlier versions of ilasm made our lives more difficult and for reasons of efficiency did not allocate one row to the param table. Thus in method a2, the first param that has no name would not be allocated a row in the param table. Thus method a2 would have only one row in the param table with a sequence number of 2. Method a3 would be worse.

 

It would be allocated no row in the param table as both params have no names or attributes. Thus we cannot assume that every param has a row in the param table. But if it has one, the sequence number field will have a value corresponding to the variable l. We had a method called GetParamAttrforMethodCalling which returned the attributes.

 

There we read from the MethodStruct  table and here we are reading from the ParamStruct table. As the code for the attributes is not very large we decided to simply copy, cut paste from the earlier method. A better way would be to create a separate method for the common code which both methods could then call.

 

Hey but we do not want to win a prize for writing politically correct code. We did not make the same mistake for the method GetParamAttrforParamMarshal where we now call the DecodeParamAttributes with the attribute byte that we read from the Param table.

 

The tabletype is 1 and the flags is 0x2000 to specify a param attribute and not a return value attribute. Now lets move on to the most important method GetParamNameForMethod that does the real work. We pass it the Method row number, the count of parameters as well as the parameter number.

 

The first problem is that we may have a situation where we have 10 methods, all of them with parameters but none of them with a name. This would give us as per the earlier compiler a empty param or no param table. Thus we first check whether our param table is empty. If yes, then we need to name the parameter ourselves.

 

The naming convention is that the parameter names start with 1 for instance methods and one less for static methods. Thus if the method has a static attribute, we reduce the parameter number stored in l by 1 and then create a name A_ along with the parameter number.

 

We then figure out as before the stating and end param table row number but as we do not reduce the startofnext by one, in the for loop we remove the equal to sign. Also the paramcount variable tells us the number of parameters as per the Method table.

 

The count variable is as per the method signature. Today both are the same, Yesterday this need not be true. As before we loop in the for statement and leave when we find a sequence number that matches our parameter count number stored in variable l. Along the way we set the paramfound parameter to true.

 

We first move to our method FillArray where we have filled up our paramnames array with the names of the parameters. This is really not necessary as all that we do is fill up the paramnames array with the name of the param stored in the name field. We then ask if the row has a param, then the name would be stored in the paramnames array.

 

At times however, the parameter has a row in the param array but the name is a null. For these cases the else must be called. The else must also be called when the param has no row un the param table. Once again the earlier rules apply, reduce by 1 if it is static method.

 

The following gives us the wrong answer for the parameter names. The sequence numbers go haywire.

 

.method public static method unmanaged stdcall bool modopt([vijay]bb) * ( method unmanaged thiscall  int8 modreq([vijay]cc) modreq([vijay]dd) *(int16,method unmanaged fastcall int32 modreq([vijay]z1) modopt([vijay]z2) * ( bool , unsigned int32) ) , int64 , unsigned int64) modreq([vijay]ee) a2(int8 aa, int16 bb)

{

}

 

Method a2 1

Param seq=4 name=aa attr=0

Param seq=2 name=bb attr=0

 

We have not finished methods yet and we have miles to go.

 

Program35.csc.txt

public void DisplayAllMethods (int typeindex)

{

if ( TypeDefStruct == null)

return;

if ( MethodStruct == null)

return;

int start , startofnext=0;

start =  TypeDefStruct[typeindex].mindex ;

if ( typeindex == (TypeDefStruct.Length -1) )

{

startofnext= MethodStruct.Length;

}

else

startofnext = TypeDefStruct[typeindex+1].mindex ;

bool placedrvazero ;

for ( int methodindex = start ; methodindex < startofnext ; methodindex++)

{

placedrvazero = false;

string methodstring = CreateSpaces(spacesforrest);

if ( IsTypeNested(typeindex))

methodstring = methodstring + CreateSpaces(spacesfornested);

methodstring = methodstring  + ".method ";

methodstring = methodstring + "/*06" + methodindex.ToString("X6") + "*/ " ;

string methodattribute = GetMethodAttribute(MethodStruct[methodindex].flags , methodindex);

int pos1 = methodattribute.IndexOf("instance");

if ( pos1 != -1)

methodattribute = methodattribute.Remove(pos1, 9);

string enterformethodattrbute = "";

string enterforreturnvalue = "";

string enterformarshal = "";

string paramattrstring = "";

string parammarshalstring = "";

paramattrstring = GetParamAttrforMethodCalling(methodindex);

parammarshalstring = GetParamAttrforMethodMarshal(methodindex , 0 );

//Console.WriteLine("..methodattribute={0} return={1} param={2} marshal={3} type={4}" , methodattribute , methoddefreturnarray[methodindex] , parammarshalstring , parammarshalstring1 , methoddeftypearray[methodindex]);

//int typeindex = GetTypeForMethod(methodindex);

string nspace= "";

nspace= NameReserved(GetString(TypeDefStruct[typeindex].nspace));

int parenttype = GetParentForNestedType(typeindex);

if ( parenttype  != 0)

nspace = NameReserved(GetString(TypeDefStruct[parenttype].nspace));

bool cat2return = false;

bool cat3returnvaluemarshal = false;

bool cat4 = false;

int howmany = HowManyMethodAttributes(methodattribute);