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;
}
Ответы
Ответ 1
Насколько я могу судить, вы не можете сделать это в .NET 4.0. Единственный способ создать тело метода без использования ILGenerator
заключается в использовании MethodBuilder.CreateMethodBody
, но это не позволяет вам устанавливать информацию об обработке исключений. И ILGenerator
заставляет команду leave
вы спрашиваете.
Однако, если .NET 4.5 является для вас вариантом (это похоже), посмотрите MethodBuilder.SetMethodBody
. Это позволяет вам самостоятельно создавать IL, но все равно передавать информацию об обработке исключений. Вы можете обернуть это в собственный ILGenerator
-подобный класс, с помощью методов Emit
, принимающих аргумент OpCode
, и чтения OpCode.Size
и OpCode.Value
, чтобы получить соответствующие байты.
И, конечно, всегда Mono.Cecil, но это, вероятно, требует более обширных изменений кода, который вы уже написали.
Изменить: вы, кажется, уже поняли это сами, но вы оставили этот вопрос открытым. Вы можете публиковать ответы на свои вопросы и принимать их, если вы сами это поняли. Это позволило бы мне знать, что я не должен был тратить время на поиски, и это позволило бы другим людям с тем же вопросом знать, что делать.