Подтвердить что ты не робот

Reflection.Emit.ILGenerator Обработка исключений "Оставить"

Во-первых, некоторая справочная информация:

Я делаю компилятор для школьного проекта. Он уже работает, и я прилагаю много усилий для исправления ошибок и/или оптимизации. Я недавно столкнулся с проблемой в том, что я обнаружил, что объект ILGenerator генерирует дополнительную команду leave при вызове любого из следующих методов-членов:

BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()

Итак, вы запускаете оператор try с вызовом BeginExceptionBlock(), добавляете пару предложений catch с BeginCatchBlock(), возможно добавляете предложение finally с BeginFinallyBlock(), а затем завершаете область защищенного кода с помощью EndExceptionBlock(),

Приведенные мной методы автоматически генерируют ветвление ветвления leave для первой команды после инструкции try. Я не хочу этого, по двум причинам. Один, потому что он всегда генерирует неоптимизированную инструкцию leave, а не инструкцию leave.s, даже если она разветвляется всего на два байта. И два, потому что вы не можете контролировать, куда идет инструкция отпуска.

Итак, если вы хотите перейти в другое место в вашем коде, вам нужно добавить локальную переменную, созданную компилятором, установить ее в зависимости от того, где вы хотите войти в оператор try, пусть EndExceptionBlock() сгенерируйте инструкцию leave, а затем сгенерируйте оператор switch ниже блока try. ИЛИ, вы можете просто генерировать инструкцию leave или leave.s самостоятельно, прежде чем вызывать один из предыдущих методов, что приводит к уродливым и недостижимым дополнительным 5 байтам, например:

L_00ca: leave.s L_00e5
L_00cc: leave L_00d1

Обе эти опции для меня неприемлемы. Есть ли способ предотвратить автоматическую генерацию инструкций leave или какой-либо другой способ указать защищенные области, а не использовать эти методы (которые крайне раздражают и практически недокументированы)?

ИЗМЕНИТЬ Примечание: сам компилятор С# делает это, так что это не так, как если бы у нас была веская причина заставить его действовать. Например, если у вас есть бета-версия .NET 4.5, разобрать следующий код и проверить их реализацию: (исключение добавлено внутренне)

public static async Task<bool> TestAsync(int ms)
{
    var local = ms / 1000;
    Console.WriteLine("In async call, before await " + local.ToString() + "-second delay.");
    await System.Threading.Tasks.Task.Delay(ms);
    Console.WriteLine("In async call, after await " + local.ToString() + "-second delay.");

    Console.WriteLine();
    Console.WriteLine("Press any key to continue.");
    Console.ReadKey(false);
    return true;
}
4b9b3361

Ответ 1

Насколько я могу судить, вы не можете сделать это в .NET 4.0. Единственный способ создать тело метода без использования ILGenerator заключается в использовании MethodBuilder.CreateMethodBody, но это не позволяет вам устанавливать информацию об обработке исключений. И ILGenerator заставляет команду leave вы спрашиваете.

Однако, если .NET 4.5 является для вас вариантом (это похоже), посмотрите MethodBuilder.SetMethodBody. Это позволяет вам самостоятельно создавать IL, но все равно передавать информацию об обработке исключений. Вы можете обернуть это в собственный ILGenerator -подобный класс, с помощью методов Emit, принимающих аргумент OpCode, и чтения OpCode.Size и OpCode.Value, чтобы получить соответствующие байты.

И, конечно, всегда Mono.Cecil, но это, вероятно, требует более обширных изменений кода, который вы уже написали.

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