Изменение имени - Name mangling

В компилятор строительство, искажение имени (также называемый имя украшения) - это метод, используемый для решения различных проблем, вызванных необходимостью разрешать уникальные имена для программных объектов во многих современных языки программирования.

Он предоставляет способ кодирования дополнительной информации от имени функция, структура, учебный класс или другой тип данных чтобы передать больше семантической информации от компилятор к компоновщик.

Необходимость изменения имен возникает, когда язык позволяет называть разные объекты одинаковыми идентификатор пока они занимают разные пространство имен (обычно определяется модулем, классом или явным пространство имен директиве) или иметь разные подписи (например, в перегрузка функции ). Это необходимо в этих случаях использования, потому что для каждой подписи могут потребоваться разные специализированные соглашение о вызовах в машинном коде.

Любой объектный код созданный компиляторами, обычно связан с другими частями объектного кода (созданного тем же или другим компилятором) с помощью типа программы, называемой компоновщик. Компоновщику требуется много информации о каждой программной сущности. Например, чтобы правильно связать функцию, ей нужно ее имя, количество аргументов и их типы и так далее.

Простые языки программирования 1970-х, такие как C, различали фрагменты кода только по имени, игнорируя любую другую информацию, например типы параметров или возвращаемые типы. C ++, определили более строгие требования к фрагментам кода, которые должны считаться «равными», такие как типы параметров, тип возвращаемого значения и соглашение о вызовах функции. Эти требования позволяют использовать перегрузку функций, а также обнаруживать различные ошибки (например, использование разных определений функции для компиляции разных исходных файлов) .Эти более строгие требования необходимы для работы с существующими инструментами и соглашениями, поэтому дополнительные требования были закодированы в имени символа, так как это была единственная информация о символе у ​​традиционного компоновщика.

Другое использование искажения имен - это обнаружение дополнительных изменений, не связанных с сигнатурой, таких как чистота функции или возможность потенциально вызвать исключение или запустить сборку мусора. Примером этого языка является D.[1][2] Это скорее упрощенная проверка ошибок. Например, функции int f (); и int g (int) чистый; могли быть скомпилированы в один объектный файл, но тогда их подписи изменились на float f (); int g (интервал); и используется для компиляции другого источника, вызывающего его. Во время связывания компоновщик обнаружит, что функция отсутствует f (int) и вернуть ошибку. Точно так же компоновщик не сможет определить, что возвращаемый тип ж отличается, и возвращает ошибку. В противном случае будут использоваться несовместимые соглашения о вызовах, что, скорее всего, приведет к неправильному результату или приведет к сбою программы. Обработка обычно не отражает всех деталей вызывающего процесса. Например, он не полностью предотвращает такие ошибки, как изменение элементов данных структуры или класса. Например, struct S {}; void f (S) {} может быть скомпилирован в один объектный файл, тогда определение для S изменено на struct S {int x; }; и используется при составлении вызова f (S ()). В таких случаях компилятор обычно использует другое соглашение о вызовах, но в обоих случаях ж будет искажать одно и то же имя, поэтому компоновщик не обнаружит эту проблему, и конечным результатом обычно будет сбой или повреждение данных или памяти во время выполнения.

Примеры

C

Хотя изменение имени обычно не требуется и не используется языками, которые не поддерживают перегрузка функции, подобно C и классический Паскаль, они используют его в некоторых случаях, чтобы предоставить дополнительную информацию о функции. Например, компиляторы, ориентированные на платформы Microsoft Windows, поддерживают множество соглашения о вызовах, которые определяют способ отправки параметров подпрограммам и возврата результатов. Поскольку различные соглашения о вызовах несовместимы друг с другом, компиляторы искажают символы кодами, детализирующими, какое соглашение следует использовать для вызова конкретной процедуры.

Схема изменения была разработана Microsoft и неофициально использовалась другими компиляторами, включая Digital Mars, Borland и GNU GCC при компиляции кода для платформ Windows. Схема применима даже к другим языкам, таким как Паскаль, D, Delphi, Фортран, и C #. Это позволяет подпрограммам, написанным на этих языках, вызывать или вызывать существующие библиотеки Windows, используя соглашение о вызовах, отличное от принятого по умолчанию.

При компиляции следующих примеров C:

int _cdecl    ж (int Икс) { возвращаться 0; }int _stdcall  грамм (int у) { возвращаться 0; }int _fastcall час (int z) { возвращаться 0; }

32-битные компиляторы выдают соответственно:

_f_g @ 4 @ h @ 4

в stdcall и быстрый звонок схемы, функция кодируется как _имя@Икс и @имя@Икс соответственно, где Икс - это количество байтов в десятичном формате аргумента (ов) в списке параметров (включая те, которые передаются в регистры, для fastcall). В случае cdecl, имя функции просто начинается с подчеркивания.

64-битное соглашение в Windows (Microsoft C) не имеет начального подчеркивания. Это различие может в некоторых редких случаях привести к нерешенным внешним факторам при переносе такого кода на 64-битную версию. Например, код Fortran может использовать псевдоним для ссылки на метод C по имени следующим образом:

ПОДРОБНЕЕ ж()! DEC $ АТРИБУТЫ C, НИКНЕЙМЫ: '_ f' :: fКОНЕЦ ПОДПРОГРАММЫ

Это будет компилировать и связывать нормально под 32 бита, но генерирует неразрешенный внешний _f под 64 бит. Один из способов решения этой проблемы - вообще не использовать «псевдоним» (в котором имена методов обычно должны быть написаны с заглавной буквы в C и Fortran). Другой вариант - использовать опцию BIND:

ПОДРОБНЕЕ ж() СВЯЗЫВАТЬ(C,ИМЯ="е")КОНЕЦ ПОДПРОГРАММЫ

В C большинство компиляторов также искажают статические функции и переменные (а в C ++ функции и переменные, объявленные статическими или помещенные в анонимное пространство имен) в единицах перевода, используя те же правила изменения, что и для их нестатических версий. Если функции с тем же именем (и параметры для C ++) также определены и используются в разных единицах перевода, это также приведет к искажению одного и того же имени, что может привести к конфликту. Однако они не будут эквивалентными, если вызываются в соответствующих единицах перевода. Компиляторы обычно могут произвольно изменять эти функции, потому что прямой доступ к ним из других единиц трансляции является незаконным, поэтому им никогда не потребуется связывание между разными объектными кодами (связывание их никогда не требуется). Чтобы предотвратить конфликты компоновки, компиляторы будут использовать стандартное искажение, но будут использовать так называемые «локальные» символы. При связывании многих таких единиц трансляции может быть несколько определений функции с одним и тем же именем, но результирующий код будет вызывать только одну или другую в зависимости от того, из какой единицы трансляции она пришла. Обычно это делается с помощью переезд механизм.

C ++

C ++ компиляторы - самые распространенные пользователи искажения имен. Первые компиляторы C ++ были реализованы как переводчики на C исходный код, который затем будет скомпилирован компилятором C в объектный код; из-за этого имена символов должны были соответствовать правилам идентификаторов C. Даже позже, с появлением компиляторов, которые производили машинный код или сборку напрямую, компоновщик обычно не поддерживал символы C ++, и все еще требовалось искажение.

В C ++ язык не определяет стандартную схему оформления, поэтому каждый компилятор использует свою собственную. C ++ также имеет сложные языковые функции, такие как классы, шаблоны, пространства имен, и перегрузка оператора, которые изменяют значение определенных символов в зависимости от контекста или использования. Мета-данные об этих функциях могут быть устранены путем изменения (украшения) имени символ. Поскольку системы изменения имен для таких функций не стандартизированы для компиляторов, немногие компоновщики могут связывать объектный код, созданный разными компиляторами.

Простой пример

Одна единица трансляции C ++ может определять две функции с именем f ():

int  ж () { возвращаться 1; }int  ж (int)  { возвращаться 0; }пустота грамм () { int я = ж(), j = ж(0); }

Это разные функции, не связанные друг с другом, кроме имени. Таким образом, компилятор C ++ закодирует информацию о типе в имени символа, и результат будет примерно таким:

int  __f_v () { возвращаться 1; }int  __f_i (int)  { возвращаться 0; } пустота __g_v () { int я = __f_v(), j = __f_i(0); }

Хотя его название уникально, грамм() все еще искажается: искажение имен применяется к все Символы C ++ (не входящие в внешний "C"{} блокировать).

Сложный пример

Искаженные символы в этом примере в комментариях под соответствующим именем идентификатора созданы компиляторами GNU GCC 3.x согласно IA-64 (Itanium) ABI:

пространство имен википедия {   учебный класс статья    {   общественный:      стандартное::нить формат ();  // = _ZN9wikipedia7article6formatEv      bool print_to (стандартное::течь&);  // = _ZN9wikipedia7article8print_toERSo      учебный класс викилинк       {      общественный:         викилинк (стандартное::нить const& имя);  // = _ZN9wikipedia7article8wikilinkC1ERKSs      };   };}

Все искаженные символы начинаются с _Z (обратите внимание, что идентификатор, начинающийся с подчеркивания, за которым следует заглавная буква, является зарезервированный идентификатор в C, чтобы избежать конфликта с идентификаторами пользователей); для вложенных имен (включая как пространства имен, так и классы) за ним следует N, затем ряд пар (длина - это длина следующего идентификатора) и, наконец, E. Например, википедия :: статья :: формат становится:

_ZN9wikipedia7article6formatE

Для функций за этим следует информация о типе; в качестве формат() это пустота функция, это просто v; следовательно:

_ZN9wikipedia7article6formatEv

За print_to, стандартный тип std :: ostream (что является typedef за std :: basic_ostream >), который имеет специальный псевдоним Так; поэтому ссылка на этот тип RSo, с полным именем функции:

_ZN9wikipedia7article8print_toERSo

Как разные компиляторы искажают одни и те же функции

Не существует стандартизированной схемы, с помощью которой искажаются даже тривиальные идентификаторы C ++, и, следовательно, разные компиляторы (или даже разные версии одного и того же компилятора или один и тот же компилятор на разных платформах) искажают общедоступные символы в радикально разных (и, следовательно, полностью несовместимых) способами. Посмотрите, как разные компиляторы C ++ искажают одни и те же функции:

Компиляторпусто ч (целое)пусто ч (число, символ)void h (недействительно)
Intel C ++ 8.0 для Linux_Z1hi_Z1hic_Z1hv
HP aC ++ A.05.55 IA-64
IAR EWARM C ++ 5.4 ARM
GCC 3.Икс и выше
Лязг 1.Икс и выше[3]
IAR EWARM C ++ 7.4 ARM_Z <число> привет_Z <число> ик_Z <число> hv
GCC 2.9.Иксh__Fih__Fich__Fv
HP aC ++ A.03.45 PA-RISC
Microsoft Visual C ++ v6-v10 (искажение деталей )? h @@ YAXH @ Z? h @@ YAXHD @ Z? h @@ YAXXZ
Цифровой Марс C ++
Borland C ++ v3.1@ h $ qi@ h $ qizc@ h $ qv
OpenVMS C ++ v6.5 (режим ARM)H__XIH__XICH__XV
OpenVMS C ++ v6.5 (режим ANSI)CXX $ __ 7H__FIC26CDH77CXX $ __ 7H__FV2CB06E8
OpenVMS C ++ X7.1 IA-64CXX $ _Z1HI2DSQ26ACXX $ _Z1HIC2NP3LI4CXX $ _Z1HV0BCA19V
SunPro CC__1cBh6Fi_v___1cBh6Fic_v___1cBh6F_v_
Tru64 C ++ v6.5 (режим ARM)h__Xih__Xich__Xv
Tru64 C ++ v6.5 (режим ANSI)__7h__Fi__7h__Fic__7h__Fv
Watcom C ++ 10.6W? H $ n (я) vW? H $ n (ia) vW? H $ n () v

Примечания:

  • Компилятор Compaq C ++ в OpenVMS VAX и Alpha (но не IA-64) и Tru64 имеет две схемы изменения имен. Первоначальная предварительная стандартная схема известна как модель ARM и основана на изменении имен, описанном в Аннотированном справочном руководстве C ++ (ARM). С появлением новых функций в стандарте C ++, в частности шаблоны, схема ARM становилась все более и более непригодной - она ​​не могла кодировать определенные типы функций или выдавать идентично искаженные имена для разных функций. Поэтому она была заменена более новой моделью «ANSI», которая поддерживала все функции шаблона ANSI, но не была обратно совместимой.
  • На IA-64 стандартный Двоичный интерфейс приложения (ABI) существует (см. внешняя ссылка ), который определяет (среди прочего) стандартную схему изменения имен и используется всеми компиляторами IA-64. GNU GCC 3.Икскроме того, принята схема изменения имен, определенная в этом стандарте, для использования на других платформах, отличных от Intel.
  • Visual Studio и Windows SDK включают программу отменить имя который печатает прототип функции в стиле C для заданного искаженного имени.
  • В Microsoft Windows компилятор Intel[4] и Лязг[5] для совместимости использует изменение имен Visual C ++.
  • Для компилятора ARM C ++ 7.4 IAR EWARM лучший способ определить имя функции - скомпилировать с включенным выходом ассемблера и просмотреть выходные данные в сгенерированном таким образом файле ".s".

Обработка символов C при линковке из C ++

Работа общей идиомы C ++:

#ifdef __cplusplus внешний "C" {#endif    /* ... */#ifdef __cplusplus}#endif

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

Например, стандартная библиотека строк, <string.h>, обычно содержит что-то вроде:

#ifdef __cplusplusвнешний "C" {#endifпустота *мемсет (пустота *, int, size_t);char *strcat (char *, const char *);int   strcmp (const char *, const char *);char *strcpy (char *, const char *);#ifdef __cplusplus}#endif

Таким образом, такой код, как:

если (strcmp(argv[1], "-Икс") == 0)     strcpy(а, argv[2]);еще     мемсет (а, 0, размер(а));

использует правильный, незапутанный strcmp и memset. Если внешний "C" не использовался, компилятор (SunPro) C ++ создаст код, эквивалентный:

если (__1cGstrcmp6Fpkc1_i_(argv[1], "-Икс") == 0)     __1cGstrcpy6Fpcpkc_0_(а, argv[2]);еще     __1cGmemset6FpviI_0_ (а, 0, размер(а));

Поскольку эти символы не существуют в библиотеке времени выполнения C (например libc) приведет к ошибкам ссылок.


Стандартизированное изменение имен в C ++

Казалось бы, стандартизованное искажение имен в языке C ++ приведет к большей совместимости между реализациями компилятора. Однако такая стандартизация сама по себе недостаточна для гарантии взаимодействия с компилятором C ++ и может даже создать ложное впечатление, что взаимодействие возможно и безопасно, когда это не так. Изменение имени - лишь одно из нескольких двоичный интерфейс приложения (ABI) детали, которые должны быть определены и соблюдены реализацией C ++. Другие аспекты ABI, такие как Обработка исключений, виртуальный стол макет, структура и кадр стека набивка, и Т. Д. также приводят к несовместимости различных реализаций C ++. Кроме того, требование определенной формы искажения вызовет проблемы для систем, в которых ограничения реализации (например, длина символов) диктуют конкретную схему преобразования. Стандартизированный требование поскольку искажение имен также предотвратит реализацию, в которой искажение вообще не требуется, например, компоновщик, который понимает язык C ++.

В Стандарт C ++ поэтому не пытается стандартизировать изменение имен. Напротив, Аннотированное справочное руководство по C ++ (также известен как РУКА, ISBN  0-201-51459-1, раздел 7.2.1c) активно поощряет использование различных схем искажения для предотвращения связывания, когда другие аспекты ABI несовместимы.

Тем не менее, как подробно описано в разделе выше, на некоторых платформах[6] полный C ++ ABI стандартизирован, включая изменение имен.

Реальные эффекты искажения имен C ++

Поскольку символы C ++ обычно экспортируются из DLL и общий объект файлов, схема изменения имен - это не просто внутреннее дело компилятора. Различные компиляторы (или разные версии одного и того же компилятора во многих случаях) создают такие двоичные файлы с разными схемами оформления имен, что означает, что символы часто не разрешаются, если компиляторы, использованные для создания библиотеки, и программа, использующая ее, использовали разные схемы. Например, если система с несколькими установленными компиляторами C ++ (например, GNU GCC и компилятор поставщика ОС) пожелала установить Библиотеки Boost C ++, его придется компилировать несколько раз (один раз для GCC и один раз для компилятора поставщика).

В целях безопасности полезно, чтобы компиляторы, производящие несовместимые объектные коды (коды, основанные на разных ABI, например, в отношении классов и исключений), использовали разные схемы изменения имен. Это гарантирует, что эти несовместимости обнаруживаются на этапе компоновки, а не при запуске программного обеспечения (что может привести к неясным ошибкам и серьезным проблемам со стабильностью).

По этой причине украшение имени является важным аспектом любого связанного с C ++ ABI.

Разобрать через фильтр c ++

$ c ++ filter -n _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_Map , Comparator , DefaultAllocator> :: has (StringName const &) const

Разобрать через встроенный GCC ABI

#включают <stdio.h>#включают <stdlib.h>#включают <cxxabi.h>int главный() {	const char *mangled_name = "_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_";	int положение дел = -1;	char *demangled_name = аби::__cxa_demangle(mangled_name, НОЛЬ, НОЛЬ, &положение дел);	printf("Распутано:% s п", demangled_name);	свободный(demangled_name);	возвращаться 0;}

Выход:

Demangled: Map , Comparator , DefaultAllocator> :: has (StringName const &) const

Ява

В Java подпись метода или класса содержит его имя и типы аргументов метода и возвращаемого значения, где это применимо. Формат подписей задокументирован, поскольку язык, компилятор и формат файла .class были разработаны вместе (и с самого начала имели в виду объектную ориентацию и универсальную совместимость).

Создание уникальных имен для внутренних и анонимных классов

Область действия анонимных классов ограничена их родительским классом, поэтому компилятор должен создать «квалифицированное» публичное имя для внутреннего класса, чтобы избежать конфликта, когда другие классы с таким же именем (внутренним или нет) существуют в том же пространстве имен. Точно так же анонимные классы должны иметь «поддельные» публичные имена, сгенерированные для них (поскольку концепция анонимных классов существует только в компиляторе, а не во время выполнения). Итак, компилируем следующую java-программу

общественный учебный класс фу {    учебный класс бар {        общественный int Икс;    }    общественный пустота Zark () {        Объект ж = новый Объект () {            общественный Нить нанизывать() {                возвращаться "Привет";            }        };    }}

произведет три .учебный класс файлы:

  • foo.class, содержащий основной (внешний) класс фу
  • foo $ bar.class, содержащий названный внутренний класс foo.bar
  • foo $ 1.класс, содержащий анонимный внутренний класс (локальный для метода foo.zark)

Все эти имена классов допустимы (поскольку символы $ разрешены в спецификации JVM), и эти имена «безопасны» для генерации компилятором, поскольку определение языка Java советует не использовать символы $ в обычных определениях классов Java.

Разрешение имен в Java еще более усложняется во время выполнения, поскольку полностью квалифицированные имена классов уникальны только внутри определенного загрузчик классов пример. Загрузчики классов упорядочены иерархически, и каждый поток в JVM имеет так называемый загрузчик классов контекста, поэтому в случаях, когда два разных экземпляра загрузчика классов содержат классы с одинаковым именем, система сначала пытается загрузить класс с помощью корневого (или системного) загрузчика классов. а затем спускается по иерархии к загрузчику класса контекста.

Собственный интерфейс Java

Поддержка собственных методов Java позволяет программам на языке Java вызывать программы, написанные на другом языке (обычно на C или C ++). Здесь есть две проблемы с разрешением имен, ни одна из которых не реализована особенно стандартным образом:

  • Преобразование JVM в родное имя - это кажется более стабильным, поскольку Oracle делает свою схему общедоступной.[7]
  • Нормальное изменение имени C ++ - см. Выше.

Python

В Python, искажение используется для "закрытых" членов класса, которые обозначаются как таковые, давая им имя с двумя ведущими знаками подчеркивания и не более чем одним подчеркиванием в конце. Например, __вещь будет искалечен, как и будет ___вещь и __вещь_, но __вещь__ и __вещь___ не буду. Среда выполнения Python не ограничивает доступ к таким членам, изменение предотвращает конфликты имен только в том случае, если производный класс определяет член с тем же именем.

При обнаружении искаженных атрибутов имени Python преобразует эти имена, добавляя перед ним один символ подчеркивания и имя включающего класса, например:

>>> учебный класс Тест:...     def __mangled_name(себя):...         проходить...     def normal_name(себя):...         проходить>>> т = Тест()>>> [attr за attr в реж(т) если "имя" в attr]['_Test__mangled_name', 'normal_name']

Паскаль

Диапазон Turbo Pascal / Delphi от Borland

Чтобы избежать искажения имен в Паскале, используйте:

экспорт  myFunc имя 'myFunc',  myProc имя 'myProc';

Free Pascal

Free Pascal поддерживает функция и перегрузка оператора, поэтому он также использует изменение имен для поддержки этих функций. С другой стороны, Free Pascal способен вызывать символы, определенные во внешних модулях, созданных на другом языке, и экспортировать свои собственные символы для вызова на другом языке. Для получения дополнительной информации обратитесь к Глава 6.2 и 7.1 из Руководство программиста Free Pascal.

Фортран

Изменение имени также необходимо в Фортран компиляторы, изначально потому что язык без учета регистра. Дальнейшие требования к искажению были наложены позже в процессе развития языка из-за добавления модули и другие функции стандарта Fortran 90. В частности, искажение регистра - распространенная проблема, с которой необходимо иметь дело, чтобы вызывать библиотеки Фортрана, такие как ЛАПАК, с других языков, например C.

Из-за нечувствительности к регистру имя подпрограммы или функции FOO должен быть преобразован компилятором в стандартизованный регистр и формат, чтобы он был связан одинаковым образом независимо от регистра. Различные компиляторы реализовали это по-разному, и никакой стандартизации не произошло. В AIX и HP-UX Компиляторы Fortran переводят все идентификаторы в нижний регистр фу, в то время как Cray и Unicos Компиляторы Fortran преобразовали идентификаторы в верхний регистр FOO. В GNU g77 компилятор преобразует идентификаторы в нижний регистр плюс подчеркивание foo_, за исключением идентификаторов, уже содержащих знак подчеркивания FOO_BAR добавить два символа подчеркивания foo_bar__, следуя конвенции, установленной f2c. Многие другие компиляторы, включая SGI с IRIX компиляторы, GNU Fortran, и Intel компилятор Fortran (кроме Microsoft Windows) преобразует все идентификаторы в нижний регистр плюс подчеркивание (foo_ и foo_bar_, соответственно). В Microsoft Windows компилятор Intel Fortran по умолчанию использует прописные буквы без подчеркивания.[8]

Идентификаторы в модулях Fortran 90 необходимо дополнительно изменить, поскольку одно и то же имя процедуры может встречаться в разных модулях. Поскольку стандарт Fortran 2003 требует, чтобы имена процедур модуля не конфликтовали с другими внешними символами,[9] компиляторы обычно используют имя модуля и имя процедуры с отдельным маркером между ними. Например:

модуль м содержитцелое число функция пять()      пять = 5   конечная функция пятьконечный модуль м

В этом модуле имя функции будет изменено как __m_MOD_five (например, GNU Fortran), m_MP_five_ (например, ifort Intel), м.five_ (например, Oracle sun95) и т. д. Поскольку Fortran не позволяет перегрузить имя процедуры, но использует общие интерфейсные блоки и общие процедуры с привязкой к типу вместо этого, искаженные имена не должны включать подсказки об аргументах.

Параметр BIND Fortran 2003 отменяет любое изменение имени, выполняемое компилятором, как показано над.

Ржавчина

Имена функций по умолчанию изменены в Ржавчина. Однако это можно отключить с помощью # [no_mangle] атрибут функции. Этот атрибут можно использовать для экспорта функций в C, C ++ или Objective-C.[10] Кроме того, вместе с #[Начните] атрибут функции или # [no_main] crate, он позволяет пользователю определять точку входа в C-стиль для программы.[11]

Rust использовал много версий схем изменения символов, которые можно выбрать во время компиляции с помощью -Z символ-искажение-версия вариант. Определены следующие манглеры:

  • наследие Изменение стиля C ++ на основе Itanium IA-64 C ++ ABI. Символы начинаются с _ZN, а хеши файлов используются для устранения неоднозначности. Используется с Rust 1.9.[12]
  • v0 Улучшенная версия устаревшей схемы с изменениями для Rust. Символы начинаются с . Полиморфизм можно закодировать. Функции не имеют закодированных возвращаемых типов (в Rust нет перегрузки). Имена Unicode используют измененные punycode. Сжатие (обратная ссылка) использует адресацию на основе байтов. Используется с Rust 1.37.[13]

Примеры представлены в Rust имена-символы тесты.[14]

Цель-C

По существу существуют две формы метода в Цель-C, то class ("статический") метод, а метод экземпляра. Объявление метода в Objective-C имеет следующую форму:

+ (возвращаемый тип) имя0:параметр0 имя1:параметр1 ...– (возвращаемый тип) имя0:параметр0 имя1:параметр1 ...

Методы класса обозначаются знаком +, методы экземпляра используют -. Тогда типичное объявление метода класса может выглядеть так:

+ (я бы) initWithX: (int) номер Энди: (int) номер;+ (я бы) новый;

С методами экземпляра, которые выглядят так:

- (я бы) ценить;- (я бы) setValue: (я бы) new_value;

Каждое из этих объявлений метода имеет определенное внутреннее представление. При компиляции каждый метод получает имя в соответствии со следующей схемой для методов класса:

_c_Учебный класс_имя0_имя1_ ...

и это, например, методы:

_я_Учебный класс_имя0_имя1_ ...

Двоеточия в синтаксисе Objective-C переводятся в символы подчеркивания. Итак, метод класса Objective-C + (я бы) initWithX: (int) номер Энди: (int) номер;, если принадлежит Точка класс будет переводиться как _c_Point_initWithX_andY_, и метод экземпляра (принадлежащий к тому же классу) - (я бы) ценить; переведет на _i_Point_value.

Таким образом помечается каждый из методов класса. Однако поиск метода, на который может ответить класс, было бы утомительным, если бы все методы были представлены таким образом. Каждому из методов присваивается уникальный символ (например, целое число). Такой символ известен как селектор. В Objective-C можно напрямую управлять селекторами - они имеют определенный тип в Objective-C - SEL.

Во время компиляции создается таблица, которая отображает текстовое представление, например _i_Point_value, селекторам (которым задан тип SEL). Управление селекторами более эффективно, чем управление текстовым представлением метода. Обратите внимание, что селектор соответствует только имени метода, а не классу, к которому он принадлежит - разные классы могут иметь разные реализации метода с одинаковым именем. Из-за этого реализациям метода также присваивается определенный идентификатор, они известны как указатели реализации, а также имеют тип, IMP.

Отправленные сообщения кодируются компилятором как вызовы я бы objc_msgSend (я бы приемник, SEL селектор, ...) функция или один из ее кузенов, где приемник является получателем сообщения, и SEL определяет метод для вызова. У каждого класса есть своя собственная таблица, которая сопоставляет селекторы с их реализациями - указатель реализации указывает, где в памяти находится фактическая реализация метода. Существуют отдельные таблицы для методов класса и экземпляра. Помимо хранения в SEL к IMP таблицы поиска, функции по существу анонимны.

В SEL значение для селектора не меняется между классами. Это позволяет полиморфизм.

Среда выполнения Objective-C хранит информацию об аргументах и ​​типах возвращаемых методов. Однако эта информация не является частью имени метода и может варьироваться от класса к классу.

Поскольку Objective-C не поддерживает пространства имен, нет необходимости изменять имена классов (которые отображаются как символы в сгенерированных двоичных файлах).

Быстрый

Swift хранит метаданные о функциях (и многом другом) в искаженных символах, относящихся к ним. Эти метаданные включают имя функции, атрибуты, имя модуля, типы параметров, тип возврата и многое другое. Например:

Искаженное имя метода функция вычислить (x: int) -> int из Мой класс класс в модуле тест является _TFC4test7MyClass9calculatefS0_FT1xSi_Si, для 2014 Swift. Компоненты и их значения следующие:[15]

  • _T: Префикс для всех символов Swift. Все начнется с этого.
  • F: Функция без каррирования.
  • C: Функция класса. (метод)
  • 4тест: Имя модуля с префиксом длины.
  • 7МойКласс: Имя класса, к которому функция принадлежит, опять же, с префиксом длины.
  • 9 вычислить: Имя функции.
  • ж: Атрибут функции. В данном случае это «f», что является нормальной функцией.
  • S0: Обозначает тип первого параметра (а именно, экземпляр класса) как первый в стеке типов (здесь Мой класс не вложен и поэтому имеет индекс 0).
  • _FT: Это начинает список типов для кортежа параметров функции.
  • 1x: Внешнее имя первого параметра функции.
  • Si: Указывает встроенный тип Swift Шаблон: Swift.Int для первого параметра.
  • _Si: Возвращаемый тип; опять таки Шаблон: Swift.Int.

Исправление для версий, начиная с Swift 4.0, официально задокументировано. Он сохраняет некоторое сходство с Itanium.[16]

Смотрите также

Рекомендации

  1. ^ «Двоичный интерфейс приложения - язык программирования D». dlang.org. Получено 2020-05-19.
  2. ^ Райнер, Шуэце. "Новомодное имя Ди" искажает ". Блог D. Получено 2020-05-19.
  3. ^ Clang - Возможности и цели: Совместимость с GCC, 15 апреля 2013 г.
  4. ^ JBIntel_deleted_06032015. «Различия OBJ между компилятором Intel и компилятором VC». software.intel.com.
  5. ^ «Совместимость с MSVC». Получено 13 мая 2016.
  6. ^ "Itanium C ++ ABI, раздел 5.1. Внешние имена (также известное как искажение)". Получено 16 мая 2016.
  7. ^ «Обзор дизайна». docs.oracle.com.
  8. ^ «Краткое изложение вопросов, связанных с использованием разных языков». Руководство пользователя и справочное руководство для компилятора Intel Fortran 15.0. Корпорация Intel. Получено 17 ноября 2014.
  9. ^ https://software.intel.com/en-us/node/510637
  10. ^ "Интерфейс внешних функций # Вызов кода Rust из C". Руководство по ржавчине. rust-lang.org. Получено 13 мая 2016.
  11. ^ "Нет stdlib". Руководство по ржавчине. rust-lang.org. Получено 13 мая 2016.
  12. ^ "rust / src / librustc_codegen_utils / symbol_names / legacy.r.rs на 57e1da59cd0761330b4ea8d47b16340a78eeafa9 · rust-lang / rust · GitHub".
  13. ^ "Искажение символа ржавчины". Книга RFC Rust.
  14. ^ "Rust 1.42.0: src / test / ui / имена-символов". Github. Получено 20 марта 2020.
  15. ^ "mikeash.com: Пятница, вопросы и ответы, 2014-08-15: Swift Name Mangling". mikeash.com.
  16. ^ "яблоко / свифт: mangling.rst". GitHub.

внешняя ссылка