Соглашения о вызовах HasThis и ExplicitThis

Я сталкиваюсь с соглашениями о вызовах HasThis и ExplicitThis на Справочный источник .NET Framework, и поэтому начинаю задаваться вопросом:

  • Когда они устанавливаются компилятором?
  • Есть ли какие-нибудь примеры использования этой комбинации соглашений о вызовах (в "реальной" управляемой программе)?

MSDN описал их как:

Явный

Specifies that the signature is a function-pointer signature, representing a call to an instance or virtual method (not a static method). If ExplicitThis is set, HasThis must also be set. The first argument passed to the called method is still a this pointer, but the type of the first argument is now unknown. Therefore, a token that describes the type (or class) of the this pointer is explicitly stored into its metadata signature.

HasThis

Specifies an instance or virtual method (not a static method). At run-time, the called method is passed a pointer to the target object as its first argument (the this pointer). The signature stored in metadata does not include the type of this first argument, because the method is known and its owner class can be discovered from metadata.

Вот пример программы, который я написал для генерации класса и конструктора с использованием этих битов:

const string FileName = "MyDynamicLib.dll";
AppDomain currentDomain = AppDomain.CurrentDomain;

AssemblyName assemblyName = new AssemblyName(assemblyName: "MyAssembly");
AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(
                                                                      name  : assemblyName,
                                                                      access: AssemblyBuilderAccess.RunAndSave
                                                                     );
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(
                                                                  name          : "MyModule",
                                                                  fileName      : FileName,
                                                                  emitSymbolInfo: true
                                                                 );

TypeBuilder typeBuilder = moduleBuilder.DefineType(
                                                   name: "MyClass",
                                                   attr: TypeAttributes.Public | TypeAttributes.BeforeFieldInit
                                                  );
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
                                                                      attributes       : MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                                                                      callingConvention: CallingConventions.HasThis | CallingConventions.ExplicitThis,
                                                                      parameterTypes   : null
                                                                     );
constructorBuilder.GetILGenerator().Emit(opcode: OpCodes.Ret);
typeBuilder.CreateType();

assemblyBuilder.Save(
                     assemblyFileName      : FileName,
                     portableExecutableKind: PortableExecutableKinds.Required32Bit,
                     imageFileMachine      : ImageFileMachine.I386
                    );

Что известно пока:
Невозможно изменить соглашение о вызове метода с помощью синтаксиса C#. Это возможно только на уровне IL, также используя Reflection emit API. HasThis и Standard используются чаще всего, объяснять их не нужно. Бит VarArgs, напротив, установлен для метода __arglist:

static void VariadicMethod(__arglist)
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
383
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Первое, что приходит в голову об этих двух флагах, - это Методы расширения, есть даже информация о как выполняется привязка этих методов во время компиляции кода.

Надеюсь, поможет!

Хорошо, давайте проверим это. Быстрое написание метода расширения и проверка его соглашений о вызовах с помощью Reflection показывает, что установлен только бит Standard, что имеет смысл, поскольку метод расширения должен быть статическим. ExplicitThis требует метода экземпляра, и, очевидно, мы не можем управлять этим битом с помощью синтаксиса C#. Итак, на уровне IL, как мы можем его использовать?

Abbecuby 27.12.2018 07:41

Тогда, возможно, явные реализации интерфейса

Itay Podhajcer 27.12.2018 07:50

Похоже, что это тоже не так. Для явного метода реализации интерфейса задан Standard | HasThis.

Abbecuby 27.12.2018 07:56

Извини, у меня в голове больше нет идей

Itay Podhajcer 27.12.2018 08:01
Ответ принят как подходящий

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

Вы можете перейти к разделу «Вернуться к вашему вопросу», чтобы увидеть окончательный ответ.


Ответ немного длинный, потому что я хочу привести ссылку, поэтому вместо того, чтобы писать свое мнение, я процитирую. Надеюсь, это будет достаточно понятно. Если нет, я отредактирую ответ с дополнительными пояснениями.

Акцент в цитатах мой. Некоторые цитаты были вырезаны.

The EXPLICITTHIS (0x40) bit can be set only in signatures for function pointers: signatures whose MethodDefSig is preceded by FNPTR

Из CoreCLR

EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli)

Указатели на функции - это способ вызова неуправляемых методов.

Unmanaged methods can also be called via function pointers.

Указатели функций могут быть вызваны с помощью инструкции calli.

Method pointer shall store the address of the entry point to a method whose signature is method-signature-compatible-with the type of the method pointer. A method can be called by using a method pointer with the calli instruction.

Подробнее об этом

Correct CIL requires that the function pointer contains the address of a method whose signature is method-signature compatible-with that specified by callsitedescr and that the arguments correctly correspond to the types of the destination function’s this pointer, if required, and parameters. For the purposes of signature matching, the HASTHIS and EXPLICITTHIS flags are ignored; all other items must be identical in the two signatures.

И

The calli instruction includes a call site description that includes information about the native calling convention that should be used to invoke the method. Correct CIL code shall specify a calling convention in the calli instruction that matches the calling convention for the method that is being called.

Описание сайта звонка

Ccall site description (represented as a metadata token for a stand-alone call signature) that provides: • The number of arguments being passed. • The data type of each argument. • The order in which they have been placed on the call stack. • The native calling convention to be used

Метод-подпись-совместимый-со средним

A method signature type T is method-signature compatible-with a method signature type U if and only if: 1. For each signature, independently, if the signature is for an instance method it carries the type of this. [Note: This is always true for the signatures of instance method pointers produced by ldvirtftn instruction. 2. The calling conventions of T and U shall match exactly, ignoring the distinction between static and instance methods (i.e., the this parameter, if any, is not treated specially).


Вернуться к вашему вопросу

  • When are they set by compiler?
  • Are there any examples using this combination of calling conventions (in "real world" managed program)?

ExplicitThisможет должен использоваться только при вызове указателей функций через инструкцию calli.

AFAIK компилятор C# нет, генерирующий инструкцию calli, поэтому вы не увидите никакого кода C#, который устанавливает этот бит.


использованная литература

Компилятор C# не генерирует инструкции calli

Исходный код Roslyn

Конверты EXPLICITTHIS и native call предназначены только для автономных подписей (для calli)

Подписи под капотом

ECMA

Спасибо за ваше время. Между тем, я решил, что это должно что-то делать с calli / ldftn, да ... Я опубликую реальный пример кода IL через некоторое время. Это очень необычно и круто.

Abbecuby 27.12.2018 16:58

Другие вопросы по теме