Этот вопрос потребует небольшого введения.
Я работаю над проектом безопасности, который будет анализировать сборки CIL и отклонять те, которые выполняют определенные определенные "плохие" действия, а также позволяет приложению хостинга предоставлять "gates "для некоторых методов, чтобы разрешить фильтрацию некоторых вызовов.(Это небольшое подмножество функциональных возможностей проекта, но об этой части я буду интересоваться здесь.)
Проект сканирует все инструкции в каждом методе сборки и ищетКоды операций call, callvirt, ldftn, ldvirtftn и newobj, поскольку это единственные коды операций, которые в конечном итоге могут привести к вызову метода.При создании делегатов используются коды операций ldftn, например:
ldarg.1
ldftn instance bool string::EndsWith(string)
newobj instance void class [System.Core]System.Func`2<string, bool>::'.ctor'(object, native int)
В конце этой последовательности Func<string, bool>
находится на вершине стека.
Скажем, что яхотите перехватить все звонки на String.EndsWith(String)
.Для call и callvirt я могу просто заменить вызов экземпляра статическим вызовом подписи Boolean(String,String)
- первым аргументом будет экземпляр строки, для которого метод был первоначально вызван.На уровне CIL поведение будет однозначным и четко определенным, поскольку именно так называются статические методы.
Но для ldftn?Я попытался просто заменить операнд инструкции ldftn тем же статическим методом, который использовался для замены операнда call / callvirt:
ldarg.1
ldftn bool class Prototype.Program::EndsWithGate(string, string)
newobj instance void class [System.Core]System.Func`2<string, bool>::'.ctor'(object, native int)
Я полностью ожидал, что это не удастся, поскольку делегат получает целевой объект (неnull) пока вручается статический указатель метода.К моему удивлению, это на самом деле работает как во время выполнения Microsoft .NET, так и в Mono.Я понимаю, что target / этот параметр является только первым параметром метода и скрыт для экземпляров методов.(Проект основан на этих знаниях.) Но тот факт, что делегаты действительно работают в таких условиях, меня немного озадачивает.
Итак, мой вопрос: это определенное и задокументированное поведение?Будут ли делегаты при вызове всегда помещать свою цель в стек, если она не равна нулю?Было бы лучше создать класс замыкания, который будет захватывать цель и «правильно» вызывать статический метод, даже если это будет намного сложнее и раздражает?