Как найти TypeSpec универсального аргумента - PullRequest
0 голосов
/ 26 февраля 2019

Некоторая предварительная информация

С учетом следующей функции C #:

public static void func<T>(T t)
{
    System.Console.WriteLine(t);
}

Он компилируется в следующий CIL:

.method public hidebysig static void  func<T>(!!T t) cil managed
{
    ldarg.0
    box        !!T
    call       void [mscorlib]System.Console::WriteLine(object)
    ret
}

Сигнатура вышеприведенного метода: 10 01 01 01 1E 00, где:

10 - Calling convention (IMAGE_CEE_CS_CALLCONV_GENERIC)
01 - Function generic arguments count (which is 1)
01 - Function arguments count (which is 1)
01 - Return type (ELEMENT_TYPE_VOID)
1E - First argument type (ELEMENT_TYPE_MVAR)
00 - Index of above MVAR (which is 0)

Также см. Следующую инструкцию с действительным байтовым кодом:

box !!T - 8C 1B000001

1B000001 указывает на первую запись втаблица TypeSpec, которая указывает на BLOB-объект 02 1E 00, где:

02 - Blob length
1E - Type type (ELEMENT_TYPE_MVAR)
00 - Index of above MVAR (which is 0)

Как мы видим, сигнатура метода содержит обобщенный аргумент описательным образом, где мы имеем фактическую сигнатуру типа.
Однако при использовании OpCode, который требует TypeDef / Ref / Spec, предоставляется TypeSpec, и TypeSpec указывает на подпись с информацией о типе.


Так что мой вопрос:
Я пишу профилировщик, который выполняет некоторую перезапись IL, и, учитывая сигнатуру функции, я хотел бы добавить несколько OpCodes в тело функции, которые будут работать с аргументами.

ИспользованиеIMetaDataImport2 интерфейс, как можетn Я получаю токен TypeSpec, который мне понадобится для заданного универсального параметра?

Я могу видеть 2 варианта:

  1. Перебирать EnumTypeSpecs до тех пор, пока не найду сигнатуру, соответствующую
  2. Использование интерфейса IMetaDataEmit для создания новой TypeSpec

Однако по понятным причинам я бы хотел избежать этих двух вариантов и выбрать более разумную альтернативу.

1 Ответ

0 голосов
/ 12 марта 2019

Итак, я закончил с моим первым предложением.
Я думаю, что это не идеально, но оно работает.

Вот код, если кому-то интересно (обработка ошибок опущена):

HCORENUM typeSpecEnum = NULL;
mdTypeSpec typeSpec = mdTypeSpecNil;
ULONG outNum = -1;

// Loop through enum
while (true)
{
    // Get next enum
    HRESULT hr = pMetadataImport->EnumTypeSpecs(&typeSpecEnum, &typeSpec, 1, &outNum);
    if (hr == S_FALSE && outNum == 0) // According to doc, this means no more. End loop
        break;

    // Get the signature of this typespec
    PCCOR_SIGNATURE curSpecSig = NULL;
    ULONG curSpecSigLen = -1;
    pMetadataImport->GetTypeSpecFromToken(typeSpec, &curSpecSig, &curSpecSigLen);

    if (curSpecSigLen == <my_len> && memcmp(curSpecSig, <my_sig>, <my_len>) == 0)
        ; // Token found
    else
        typeSpec = mdTypeSpecNil; // Reset and goto next token
}

pMetadataImport->CloseEnum(typeSpecEnum); // Don't forget to close enum
...