To pytanie wymaga nieco wstępu.Zachowanie delegata CIL ze sprzeczną "statycznością" metody docelowej
Pracuję nad projektem bezpieczeństwa, który przeanalizuje złożenia CIL i odrzuci te, które wykonują pewne określone "złe" rzeczy, jednocześnie umożliwiając aplikacji hostującej dostarczanie "bramek" dla niektórych metod, aby umożliwić filtrowanie niektórych połączeń. (Jest to niewielki podzbiór funkcjonalności projektu, ale jest to część, o którą będę tutaj pytać.)
Projekt przeszukuje wszystkie instrukcje w każdej metodzie w zespole i szuka połączenia, callcirt, ldftn, ldvirtftn i newobj opcodes, ponieważ są to jedyne opkody, które mogą ostatecznie spowodować wywołanie metody. Te rozkazy ldftn są wykorzystywane przy konstruowaniu delegatów, tak:
ldarg.1
ldftn instance bool string::EndsWith(string)
newobj instance void class [System.Core]System.Func`2<string, bool>::'.ctor'(object, native int)
Pod koniec tej sekwencji Func<string, bool>
znajduje się na szczycie stosu.
Załóżmy, że chcę przechwycić wszystkie połączenia z numerem String.EndsWith(String)
. W przypadku wywołania i callvirt, mogę po prostu zastąpić wywołanie instancji statycznym wywołaniem sygnatury Boolean(String,String)
- pierwszym argumentem będzie instancja napisu, na której metoda była pierwotnie wywoływana. Na poziomie CIL zachowanie będzie jednoznaczne i dobrze zdefiniowane, ponieważ w ten sposób wywoływane są metody statyczne.
Ale czy na pewno? Próbowałem po prostu zastępując operandu instrukcji ldftn z tej samej metody statyczne stosowane w celu zastąpienia CALL/callvirt za argument:
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)
byłem w pełni spodziewając to się nie powiedzie, ponieważ pełnomocnik otrzymuje obiekt docelowy (not null) podczas wręczania statycznego wskaźnika metody. Ku mojemu zaskoczeniu działa to zarówno w środowisku wykonawczym Microsoft .NET, jak i Mono. Rozumiem, że obiekt docelowy/ten parametr jest tylko pierwszym parametrem metody i jest ukryty dla metod instancji. (Projekt opiera się na tej wiedzy.) Ale fakt, że delegaci faktycznie działają w tych okolicznościach, jest dla mnie nieco zagadkowy.
Moje pytanie brzmi: czy to zdefiniowane i udokumentowane zachowanie? Czy delegaci, jeśli zostaną przywołani, zawsze przesuwają swój cel na stos, jeśli nie jest on zerowy? Czy byłoby lepiej skonstruować klasę zamknięcia, która uchwyci cel i "poprawnie" wywoła metodę statyczną, mimo że będzie to o wiele bardziej skomplikowane i denerwujące?
Bez tego zachowania nie można stworzyć delegata z metod rozszerzenie tak jak od metody instancji. – CodesInChaos