stringtranslate.com

Mutilación de nombres

En la construcción de compiladores , la alteración de nombres (también llamada decoración de nombres ) es una técnica utilizada para resolver diversos problemas causados ​​por la necesidad de resolver nombres únicos para entidades de programación en muchos lenguajes de programación modernos .

Proporciona medios para codificar información agregada en el nombre de una función , estructura , clase u otro tipo de datos , para pasar más información semántica del compilador al enlazador .

La necesidad de alterar nombres surge cuando un lenguaje permite que diferentes entidades se denominen con el mismo identificador siempre que ocupen un espacio de nombres diferente (normalmente definido por un módulo, una clase o una directiva explícita de espacio de nombres ) o tengan diferentes firmas de tipo (como en la sobrecarga de funciones ). Es necesario en estos usos porque cada firma puede requerir una convención de llamada especializada diferente en el código de máquina .

Cualquier código objeto producido por compiladores suele estar vinculado con otros fragmentos de código objeto (producidos por el mismo compilador o por otro) mediante un tipo de programa llamado enlazador . El enlazador necesita una gran cantidad de información sobre cada entidad del programa. Por ejemplo, para vincular correctamente una función necesita su nombre, el número de argumentos y sus tipos, etc.

Los lenguajes de programación simples de la década de 1970, como C , solo distinguían las subrutinas por su nombre, ignorando otra información, incluidos los tipos de parámetros y de retorno. Los lenguajes posteriores, como C++ , definieron requisitos más estrictos para que las rutinas se consideraran "iguales", como los tipos de parámetros, el tipo de retorno y la convención de llamada de una función. Estos requisitos permiten la sobrecarga de métodos y la detección de algunos errores (como el uso de diferentes definiciones de una función al compilar diferentes archivos de código fuente ). Estos requisitos más estrictos debían funcionar con las herramientas y convenciones de programación existentes . Por lo tanto, los requisitos agregados se codificaron en el nombre del símbolo, ya que esa era la única información que un enlazador tradicional tenía sobre un símbolo.

Ejemplos

do

Aunque la manipulación de nombres no suele ser necesaria ni utilizada por lenguajes que no admiten la sobrecarga de funciones , como C y el Pascal clásico , sí la utilizan en algunos casos para proporcionar información adicional sobre una función. Por ejemplo, los compiladores destinados a las plataformas Microsoft Windows admiten una variedad de convenciones de llamada , que determinan la forma en que se envían los parámetros a las subrutinas y se devuelven los resultados. Debido a que las diferentes convenciones de llamada son incompatibles entre sí, los compiladores manipulan los símbolos con códigos que detallan qué convención se debe utilizar para llamar a la rutina específica.

El esquema de mutilación para Windows fue establecido por Microsoft y ha sido seguido informalmente por otros compiladores, incluidos Digital Mars , Borland y GNU Compiler Collection (GCC) al compilar código para las plataformas Windows. El esquema incluso se aplica a otros lenguajes, como Pascal , D , Delphi , Fortran y C# . Esto permite que las subrutinas escritas en esos lenguajes llamen, o sean llamadas por, bibliotecas de Windows existentes utilizando una convención de llamada diferente de su convención de llamada predeterminada.

Al compilar los siguientes ejemplos de C:

int _cdecl f ( int x ) { devuelve 0 ; } int _stdcall g ( int y ) { devuelve 0 ; } int _fastcall h ( int z ) { devuelve 0 ; }                        

Los compiladores de 32 bits emiten, respectivamente:

_F_g@4@h@4

En los esquemas de manipulación stdcally fastcall, la función se codifica como y respectivamente, donde X es la cantidad de bytes, en decimal, del argumento o argumentos en la lista de parámetros (incluidos los que se pasan en registros, para fastcall). En el caso de , el nombre de la función simplemente tiene como prefijo un guión bajo._name@X@name@Xcdecl

La convención de 64 bits en Windows (Microsoft C) no tiene guión bajo inicial. Esta diferencia puede, en algunos casos excepcionales, generar errores externos no resueltos al trasladar dicho código a 64 bits. Por ejemplo, el código Fortran puede usar "alias" para vincularse con un método C por nombre, de la siguiente manera:

SUBRUTINA f () !DEC$ ATRIBUTOS C, ALIAS:'_f' :: f FIN SUBRUTINA

Esto compilará y vinculará correctamente en 32 bits, pero generará un externo no resuelto _fen 64 bits. Una solución alternativa para esto es no usar "alias" en absoluto (en cuyo caso los nombres de los métodos normalmente deben escribirse con mayúscula en C y Fortran). Otra es usar la opción BIND:

SUBRUTINA f () BIND ( C , NOMBRE = "f" ) FIN DE SUBRUTINA 

En C, la mayoría de los compiladores también alteran funciones y variables estáticas (y en C++ funciones y variables declaradas estáticas o puestas en el espacio de nombres anónimo) en unidades de traducción utilizando las mismas reglas de alteración que para sus versiones no estáticas. Si también se definen y utilizan funciones con el mismo nombre (y parámetros para C++) en diferentes unidades de traducción, también se alterará el mismo nombre, lo que puede provocar un conflicto. Sin embargo, no serán equivalentes si se las llama en sus respectivas unidades de traducción. Los compiladores suelen tener la libertad de emitir alteraciones arbitrarias para estas funciones, porque es ilegal acceder a ellas directamente desde otras unidades de traducción, por lo que nunca necesitarán vincularse entre diferentes códigos objeto (nunca es necesario vincularlas). Para evitar conflictos de vinculación, los compiladores utilizarán la alteración estándar, pero utilizarán los llamados símbolos "locales". Al vincular muchas de estas unidades de traducción, puede haber múltiples definiciones de una función con el mismo nombre, pero el código resultante solo llamará a una u otra según la unidad de traducción de la que provenga. Esto se hace normalmente utilizando el mecanismo de reubicación .

C++

Los compiladores de C++ son los usuarios más extendidos de la manipulación de nombres. Los primeros compiladores de C++ se implementaron como traductores de código fuente de C , que luego sería compilado por un compilador de C a código objeto; debido a esto, los nombres de los símbolos tenían que cumplir con las reglas de identificadores de C. Incluso más tarde, con la aparición de compiladores que producían código de máquina o ensamblador directamente, el enlazador del sistema generalmente no admitía símbolos de C++ y la manipulación seguía siendo necesaria.

El lenguaje C++ no define un esquema de decoración estándar, por lo que cada compilador utiliza el suyo propio. C++ también tiene características de lenguaje complejas, como clases , plantillas , espacios de nombres y sobrecarga de operadores , que alteran el significado de símbolos específicos según el contexto o el uso. Los metadatos sobre estas características se pueden desambiguar alterando (decorando) el nombre de un símbolo . Debido a que los sistemas de alteración de nombres para tales características no están estandarizados en todos los compiladores, pocos enlazadores pueden vincular código objeto producido por diferentes compiladores.

Ejemplo sencillo

Una única unidad de traducción de C++ podría definir dos funciones denominadas f():

int f () { devuelve 1 ; } int f ( int ) { devuelve 0 ; } void g () { int i = f (), j = f ( 0 ); }                       

Se trata de funciones distintas, sin relación entre sí más allá del nombre. Por lo tanto, el compilador de C++ codificará la información de tipo en el nombre del símbolo, y el resultado será algo parecido a lo siguiente:

int __f_v () { devuelve 1 ; } int __f_i ( int ) { devuelve 0 ; } void __g_v () { int i = __f_v (), j = __f_i ( 0 ); }                        

Aunque su nombre es único, g()aún así está alterado: la alteración de nombres se aplica a todos los símbolos de C++ (excepto aquellos en un bloque).extern "C"{}

Ejemplo complejo

Los símbolos alterados en este ejemplo, en los comentarios debajo del nombre del identificador respectivo, son los producidos por los compiladores GNU GCC 3.x, según la ABI IA-64 (Itanium):

espacio de nombres wikipedia { clase artículo { público : std :: cadena formato (); // = _ZN9wikipedia7article6formatEv            bool imprimir_a ( std :: ostream & ); // = _ZN9wikipedia7article8imprimir_aERSo    clase wikilink { público : wikilink ( std :: string const & nombre ); // = _ZN9wikipedia7article8wikilinkC1ERKSs }; }; }           

Todos los símbolos alterados comienzan con _Z(tenga en cuenta que un identificador que comienza con un guión bajo seguido de una letra mayúscula es un identificador reservado en C, por lo que se evita el conflicto con los identificadores de usuario); para los nombres anidados (incluidos tanto los espacios de nombres como las clases), esto va seguido de N, luego una serie de pares <length, id> (la longitud es la longitud del siguiente identificador) y, finalmente E, . Por ejemplo, wikipedia::article::formatse convierte en:

_ZN9wikipedia7artículo6formatoE

Para las funciones, a esto le sigue la información de tipo; como format()es una voidfunción, esto es simplemente v; por lo tanto:

_ZN9wikipedia7artículo6formatoEv

Para , se utiliza print_toel tipo estándar std::ostream(que es un typedef para ), que tiene el alias especial ; por lo tanto, una referencia a este tipo es , siendo el nombre completo de la función:std::basic_ostream<char, std::char_traits<char> >SoRSo

_ZN9wikipedia7article8print_toERSo

Cómo diferentes compiladores alteran las mismas funciones

No existe un esquema estandarizado mediante el cual se alteren incluso los identificadores triviales de C++ y, en consecuencia, distintos compiladores (o incluso distintas versiones del mismo compilador, o el mismo compilador en distintas plataformas) alteran los símbolos públicos de maneras radicalmente diferentes (y, por lo tanto, totalmente incompatibles). Consideremos cómo distintos compiladores de C++ alteran las mismas funciones:

Notas:

Manejo de símbolos C al vincular desde C++

El trabajo del idioma común de C++:

#ifdef __cplusplus externo "C" { #endif /* ... */ #ifdef __cplusplus } #endif   

es asegurar que los símbolos dentro estén "desenredados" – que el compilador emita un archivo binario con sus nombres sin decorar, como lo haría un compilador de C. Como las definiciones del lenguaje C están desenredadas, el compilador de C++ necesita evitar enredar las referencias a estos identificadores.

Por ejemplo, la biblioteca de cadenas estándar, <string.h>, generalmente contiene algo parecido a:

#ifdef __cplusplus externo "C" { #endif  vacío * memset ( vacío * , int , tamaño_t ); char * strcat ( char * , const char * ); int strcmp ( const char * , const char * ); char * strcpy ( char * , const char * );                        #ifdef __cplusplus } #finsi

Así, un código como el siguiente:

si ( strcmp ( argv [ 1 ], "-x" ) == 0 ) strcpy ( a , argv [ 2 ]); de lo contrario memset ( a , 0 , sizeof ( a ));            

utiliza el strcmpy correcto, sin modificar memset. Si extern "C"no se hubiera utilizado, el compilador de C++ (SunPro) produciría un código equivalente a:

si ( __1cGstrcmp6Fpkc1_i_ ( argv [ 1 ], "-x" ) == 0 ) __1cGstrcpy6Fpcpkc_0_ ( a , argv [ 2 ]); de lo contrario __1cGmemset6FpviI_0_ ( a , 0 , sizeof ( a ));            

Dado que esos símbolos no existen en la biblioteca de ejecución de C ( por ejemplo, libc), se producirían errores de enlace.


Manipulación de nombres estandarizada en C++

Parecería que la manipulación de nombres estandarizada en el lenguaje C++ conduciría a una mayor interoperabilidad entre las implementaciones del compilador. Sin embargo, dicha estandarización por sí sola no sería suficiente para garantizar la interoperabilidad del compilador de C++ e incluso podría crear una falsa impresión de que la interoperabilidad es posible y segura cuando no lo es. La manipulación de nombres es solo uno de los varios detalles de la interfaz binaria de aplicación (ABI) que una implementación de C++ debe decidir y observar. Otros aspectos de la ABI como el manejo de excepciones , el diseño de la tabla virtual , la estructura y el relleno del marco de pila también hacen que las diferentes implementaciones de C++ sean incompatibles. Además, requerir una forma particular de manipulación causaría problemas para los sistemas donde los límites de implementación (por ejemplo, la longitud de los símbolos) dictan un esquema de manipulación particular. Un requisito estandarizado para la manipulación de nombres también evitaría una implementación donde la manipulación no fuera necesaria en absoluto, por ejemplo, un enlazador que entendiera el lenguaje C++.

Por lo tanto, el estándar C++ no intenta estandarizar la manipulación de nombres. Por el contrario, el Manual de referencia de C++ anotado (también conocido como ARM , ISBN  0-201-51459-1 , sección 7.2.1c) fomenta activamente el uso de diferentes esquemas de manipulación para evitar la vinculación cuando otros aspectos de la ABI son incompatibles.

Sin embargo, como se detalla en la sección anterior, en algunas plataformas [4] se ha estandarizado la ABI completa de C++, incluida la alteración de nombres.

Efectos en el mundo real de la alteración de nombres en C++

Dado que los símbolos de C++ se exportan rutinariamente desde archivos DLL y de objetos compartidos , el esquema de alteración de nombres no es simplemente un asunto interno del compilador. Diferentes compiladores (o diferentes versiones del mismo compilador, en muchos casos) producen dichos binarios bajo diferentes esquemas de decoración de nombres, lo que significa que los símbolos con frecuencia quedan sin resolver si los compiladores utilizados para crear la biblioteca y el programa que la utiliza emplearon esquemas diferentes. Por ejemplo, si un sistema con varios compiladores de C++ instalados (por ejemplo, GNU GCC y el compilador del proveedor del sistema operativo) deseara instalar las bibliotecas Boost C++ , tendría que compilarse varias veces (una para GCC y otra para el compilador del proveedor).

Por razones de seguridad, es bueno que los compiladores que producen códigos de objetos incompatibles (códigos basados ​​en diferentes ABI, por ejemplo, en lo que respecta a clases y excepciones) utilicen diferentes esquemas de manipulación de nombres. Esto garantiza que estas incompatibilidades se detecten en la fase de vinculación, no durante la ejecución del software (lo que podría provocar errores ocultos y problemas graves de estabilidad).

Por esta razón, la decoración de nombres es un aspecto importante de cualquier ABI relacionada con C++ .

Existen casos, particularmente en bases de código grandes y complejas, en los que puede resultar difícil o poco práctico asignar el nombre alterado emitido dentro de un mensaje de error del enlazador al token/nombre de variable correspondiente en el código fuente. Este problema puede hacer que la identificación de los archivos fuente relevantes sea muy difícil para los ingenieros de compilación o prueba, incluso si solo se utilizan un compilador y enlazador. Los desmanteladores (incluidos los que se encuentran dentro de los mecanismos de notificación de errores del enlazador) a veces ayudan, pero el mecanismo de desmantelamiento en sí mismo puede descartar información crítica de desambiguación.

Desenredar mediante c++filt

$ c++filt  -n  _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_ Mapa<NombreDeCadena, Ref<GDScript>, Comparador<NombreDeCadena>, DefaultAllocator>::has(StringName const&) const

Desenredar mediante la ABI GCC incorporada

#incluir <stdio.h> #incluir <stdlib.h> #incluir <cxxabi.h>   int main () { const char * nombre_destrozado = "_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_" ; int estado = -1 ; char * nombre_destrozado = abi :: __cxa_demangle ( nombre_destrozado , NULL , NULL , & estado ); printf ( "Destrozado: %s \n " , nombre_destrozado ); free ( nombre_destrozado ); return 0 ; }                 

Producción:

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

Java

En Java , la firma de un método o una clase contiene su nombre y los tipos de sus argumentos de método y el valor de retorno, cuando corresponda. El formato de las firmas está documentado, ya que el lenguaje, el compilador y el formato de archivo .class se diseñaron juntos (y tenían en mente la orientación a objetos y la interoperabilidad universal desde el principio).

Creación de nombres únicos para clases internas y anónimas

El alcance de las clases anónimas se limita a su clase padre, por lo que el compilador debe producir un nombre público "calificado" para la clase interna , para evitar conflictos cuando existen otras clases con el mismo nombre (internas o no) en el mismo espacio de nombres. De manera similar, las clases anónimas deben tener nombres públicos "falsos" generados para ellas (ya que el concepto de clases anónimas solo existe en el compilador, no en el entorno de ejecución). Por lo tanto, compilamos el siguiente programa Java:

clase pública foo { clase bar { int público x ; }           public void zark () { Objeto f = nuevo Objeto () { public String toString () { return "hola" ; } }; } }                    

producirá tres archivos .class :

Todos estos nombres de clase son válidos (ya que los símbolos $ están permitidos en la especificación JVM) y estos nombres son "seguros" para que el compilador los genere, ya que la definición del lenguaje Java aconseja no utilizar símbolos $ en las definiciones de clases Java normales.

La resolución de nombres en Java se complica aún más en tiempo de ejecución, ya que los nombres completos de las clases son únicos solo dentro de una instancia específica del cargador de clases . Los cargadores de clases están ordenados jerárquicamente y cada subproceso en la JVM tiene un cargador de clases de contexto, por lo que en los casos en que dos instancias de cargadores de clases diferentes contienen clases con el mismo nombre, el sistema primero intenta cargar la clase utilizando el cargador de clases raíz (o del sistema) y luego desciende por la jerarquía hasta el cargador de clases de contexto.

Interfaz nativa de Java

Interfaz nativa de Java , el soporte de métodos nativos de Java, permite que los programas en lenguaje Java llamen a programas escritos en otro lenguaje (normalmente C o C++). Aquí hay dos problemas de resolución de nombres, ninguno de los cuales se implementa de manera estandarizada :

Pitón

En Python , la manipulación se utiliza para los atributos de clase que no se desea que utilicen las subclases [6], que se designan como tales al darles un nombre con dos o más guiones bajos iniciales y no más de un guion bajo final. Por ejemplo, __thingwill se manipulará, como will ___thingy __thing_, pero __thing__y __thing___no. El entorno de ejecución de Python no restringe el acceso a dichos atributos, la manipulación solo evita las colisiones de nombres si una clase derivada define un atributo con el mismo nombre.

Al encontrar atributos con nombres alterados, Python transforma estos nombres anteponiendo un solo guión bajo y el nombre de la clase que los contiene, por ejemplo:

>>> clase  Prueba : ...  def  __mangled_name ( self ): ...  pass ...  def  nombre_normal ( self ): ...  pass >>> t  =  Prueba () >>> [ atributo  para  atributo  en  dir ( t )  si  "nombre"  en  atributo ] ['_Test__mangled_name', 'nombre_normal']

Pascal

Turbo Pascal, Delphi

Para evitar la alteración de nombres en Pascal, utilice:

exporta myFunc nombre 'myFunc' , myProc nombre 'myProc' ;      

Pascal libre

Free Pascal admite la sobrecarga de funciones y operadores, por lo que también utiliza la manipulación de nombres para admitir estas funciones. Por otro lado, Free Pascal es capaz de llamar símbolos definidos en módulos externos creados con otro lenguaje y exportar sus propios símbolos para que sean llamados por otro lenguaje. Para obtener más información, consulte los capítulos 6.2 y 7.1 de la Guía del programador de Free Pascal.

Fortran

La alteración de nombres también es necesaria en los compiladores Fortran , originalmente porque el lenguaje no distingue entre mayúsculas y minúsculas . Más tarde, en la evolución del lenguaje, se impusieron otros requisitos de alteración debido a la incorporación de módulos y otras características en el estándar Fortran 90. La alteración de mayúsculas y minúsculas, en particular, es un problema común que debe solucionarse para llamar a bibliotecas Fortran, como LAPACK , desde otros lenguajes , como C.

Debido a la falta de distinción entre mayúsculas y minúsculas, el nombre de una subrutina o función FOOdebe ser convertido a un formato y mayúsculas estandarizados por el compilador para que se vincule de la misma manera independientemente de las mayúsculas y minúsculas. Diferentes compiladores han implementado esto de diversas maneras, y no se ha producido ninguna estandarización. Los compiladores Fortran de AIX y HP-UX convierten todos los identificadores a minúsculas foo, mientras que los compiladores Fortran de Cray y UnicosFOO convirtieron los identificadores a todos en mayúsculas . El compilador GNU g77 convierte los identificadores a minúsculas más un guión bajo foo_, excepto que los identificadores que ya contienen un guión bajo FOO_BARtienen dos guiones bajos añadidos foo_bar__, siguiendo una convención establecida por f2c . Muchos otros compiladores, incluidos los compiladores IRIX de Silicon Graphics (SGI) , GNU Fortran y el compilador Fortran de Intel (excepto en Microsoft Windows), convierten todos los identificadores a minúsculas más un guión bajo ( y , respectivamente). En Microsoft Windows, el compilador Fortran de Intel tiene como valor predeterminado mayúsculas sin guión bajo. [7]foo_foo_bar_

Los identificadores de los módulos Fortran 90 deben modificarse aún más, ya que el mismo nombre de procedimiento puede aparecer en diferentes módulos. Dado que el estándar Fortran 2003 exige que los nombres de los procedimientos de los módulos no entren en conflicto con otros símbolos externos, [8] los compiladores tienden a utilizar el nombre del módulo y el nombre del procedimiento, con un marcador distinto entre ellos. Por ejemplo:

módulo m contiene función entera cinco () cinco = 5 fin de función cinco fin del módulo m      

En este módulo, el nombre de la función se alterará como __m_MOD_five(por ejemplo, GNU Fortran), m_MP_five_(por ejemplo, ifort de Intel), m.five_(por ejemplo, sun95 de Oracle), etc. Dado que Fortran no permite sobrecargar el nombre de un procedimiento, sino que utiliza bloques de interfaz genéricos y procedimientos genéricos limitados por tipo, los nombres alterados no necesitan incorporar pistas sobre los argumentos.

La opción BIND de Fortran 2003 anula cualquier alteración de nombres realizada por el compilador, como se muestra arriba.

Óxido

Los nombres de las funciones se modifican de forma predeterminada en Rust . Sin embargo, esto se puede desactivar mediante el #[no_mangle]atributo function. Este atributo se puede utilizar para exportar funciones a C, C++ u Objective-C . [9] Además, junto con el #[start]atributo function o el #[no_main]atributo crate, permite al usuario definir un punto de entrada de estilo C para el programa. [10]

Rust ha utilizado muchas versiones de esquemas de manipulación de símbolos que pueden seleccionarse en tiempo de compilación con una -Z symbol-mangling-versionopción. Se definen los siguientes manipuladores:

Se proporcionan ejemplos en las symbol-namespruebas de Rust. [13]

Objetivo-C

Básicamente, existen dos formas de método en Objective-C : el método de clase ("estático") y el método de instancia . Una declaración de método en Objective-C tiene el siguiente formato:

+ ( tipo-de-retorno ) nombre 0 : parámetro 0  nombre 1 : parámetro 1 ...– ( tipo-de-retorno ) nombre 0 : parámetro 0  nombre 1 : parámetro 1 ...

Los métodos de clase se indican con +, los métodos de instancia utilizan -. Una declaración de método de clase típica podría verse así:

+  ( id )  initWithX: ( int ) número andY: ( int ) número ; + ( id ) nuevo ;       

Con métodos de instancia que se ven así:

-  ( id )  valor ; -  ( id )  establecerValor: ( id ) nuevo_valor ;  

Cada una de estas declaraciones de métodos tiene una representación interna específica. Al compilarse, cada método se nombra según el siguiente esquema para métodos de clase:

_c_ Clase _ nombre 0 _ nombre 1 _ ...

Y estos por ejemplo métodos:

_i_ Clase _ nombre 0 _ nombre 1 _ ...

Los dos puntos en la sintaxis Objective-C se traducen en guiones bajos. Por lo tanto, el método de clase Objective-C , si pertenece a la clase, se traduciría como , y el método de instancia (que pertenece a la misma clase) se traduciría como .+ (id) initWithX: (int) number andY: (int) number;Point_c_Point_initWithX_andY_- (id) value;_i_Point_value

Cada uno de los métodos de una clase se etiqueta de esta manera. Sin embargo, buscar un método al que una clase puede responder sería tedioso si todos los métodos estuvieran representados de esta manera. A cada uno de los métodos se le asigna un símbolo único (como un entero). Este símbolo se conoce como selector . En Objective-C, se pueden gestionar los selectores directamente (tienen un tipo específico en Objective-C) SEL.

Durante la compilación, se crea una tabla que asigna la representación textual, como _i_Point_value, a los selectores (a los que se les asigna un tipo SEL). Administrar selectores es más eficiente que manipular la representación textual de un método. Tenga en cuenta que un selector solo coincide con el nombre de un método, no con la clase a la que pertenece: diferentes clases pueden tener diferentes implementaciones de un método con el mismo nombre. Debido a esto, a las implementaciones de un método también se les asigna un identificador específico, estos se conocen como punteros de implementación, y también se les asigna un tipo, IMP.

Los mensajes enviados son codificados por el compilador como llamadas a la función, o una de sus primas, donde es el receptor del mensaje y determina el método a llamar. Cada clase tiene su propia tabla que asigna los selectores a sus implementaciones: el puntero de implementación especifica en qué parte de la memoria reside la implementación del método. Hay tablas separadas para los métodos de clase y de instancia. Además de estar almacenadas en las tablas de búsqueda, las funciones son esencialmente anónimas.id objc_msgSend (id receiver, SEL selector, ...)receiverSELSELIMP

El SELvalor de un selector no varía entre clases, lo que permite el polimorfismo .

El entorno de ejecución Objective-C mantiene información sobre los tipos de argumentos y de retorno de los métodos. Sin embargo, esta información no forma parte del nombre del método y puede variar de una clase a otra.

Dado que Objective-C no admite espacios de nombres , no es necesario alterar los nombres de clases (que aparecen como símbolos en los binarios generados).

Rápido

Swift conserva metadatos sobre funciones (y más) en los símbolos modificados que hacen referencia a ellas. Estos metadatos incluyen el nombre de la función, los atributos, el nombre del módulo, los tipos de parámetros, el tipo de retorno y más. Por ejemplo:

El nombre modificado de un método func calculate(x: int) -> intde una MyClassclase en el módulo testes _TFC4test7MyClass9calculatefS0_FT1xSi_Si, para Swift 2014. Los componentes y sus significados son los siguientes: [14]

La manipulación de versiones posteriores a Swift 4.0 está documentada oficialmente y conserva algunas similitudes con Itanium. [15]

Véase también

Referencias

  1. ^ Clang - Características y objetivos: compatibilidad con GCC, 15 de abril de 2013
  2. ^ JBIntel_deleted_06032015. "Diferencias OBJ entre el compilador Intel y el compilador VC". software.intel.com .{{cite web}}: CS1 maint: numeric names: authors list (link)
  3. ^ "Compatibilidad con MSVC" . Consultado el 13 de mayo de 2016 .
  4. ^ "Itanium C++ ABI, sección 5.1 Nombres externos (también conocido como Mangling)" . Consultado el 16 de mayo de 2016 .
  5. ^ "Descripción general del diseño". docs.oracle.com .
  6. ^ "PEP 8 – Guía de estilo para código Python".
  7. ^ "Resumen de problemas relacionados con lenguajes mixtos". Guía de referencia y del usuario para el compilador Intel Fortran 15.0 . Intel Corporation . Consultado el 17 de noviembre de 2014 .
  8. ^ "Biblioteca de documentación".
  9. ^ "Interfaz de función externa # Llamar código Rust desde C". Manual de Rust . rust-lang.org . Consultado el 13 de mayo de 2016 .
  10. ^ "No stdlib". Manual de Rust . rust-lang.org . Consultado el 13 de mayo de 2016 .
  11. ^ "rust/src/librustc_codegen_utils/symbol_names/legacy.r.rs en 57e1da59cd0761330b4ea8d47b16340a78eeafa9 · rust-lang/rust · GitHub". GitHub . 3 de noviembre de 2021.
  12. ^ "La desfiguración de los símbolos de Rust". El libro RFC de Rust .
  13. ^ "Rust 1.42.0: src/test/ui/symbol-names". Github . Consultado el 20 de marzo de 2020 .
  14. ^ "mikeash.com: Preguntas y respuestas del viernes 15 de agosto de 2014: Alteración del nombre de Swift". mikeash.com .
  15. ^ "apple/swift: mangling.rst". GitHub . 3 de noviembre de 2021.

Enlaces externos