stringtranslate.com

Manipulació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 vinculador .

La necesidad de modificar nombres surge cuando un lenguaje permite que diferentes entidades sean nombradas con el mismo identificador siempre que ocupen un espacio de nombres diferente (generalmente definido por un módulo, clase o directiva explícita de espacio de nombres ) o tengan firmas de tipo diferentes (como en 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 generalmente se vincula con otras piezas de código objeto (producidas por el mismo compilador u otro) mediante un tipo de programa llamado vinculador . El vinculador necesita una gran cantidad de información sobre cada entidad del programa. Por ejemplo, para vincular correctamente una función necesita su nombre, la cantidad 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 parámetros y tipos de retorno. Los lenguajes posteriores, como C++ , definieron requisitos más estrictos para que las rutinas se consideren "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, se codificaron requisitos adicionales en el nombre del símbolo, ya que esa era la única información que tenía un enlazador tradicional sobre un símbolo.

Otro uso de la manipulación de nombres es para detectar cambios agregados no relacionados con la firma, como la pureza de la función, o si potencialmente puede generar una excepción o desencadenar la recolección de basura . Un ejemplo de un lenguaje que hace esto es D. [1] [2] Se trata más bien de una comprobación de errores simplificada. Por ejemplo, las funciones int f();y int g(int) pure;podrían compilarse en un archivo objeto, pero luego sus firmas cambiaron float f(); int g(int);y se usaron para compilar otra fuente que lo llame. En el momento del enlace, el enlazador detectará que no hay ninguna función f(int)y devolverá un error. De manera similar, el vinculador no podrá detectar que el tipo de retorno fes diferente y devolverá un error. De lo contrario, se utilizarían convenciones de llamada incompatibles y muy probablemente producirían un resultado incorrecto o bloquearían el programa. Mangling no suele capturar todos los detalles del proceso de llamada. Por ejemplo, no previene por completo errores como cambios de miembros de datos de una estructura o clase. Por ejemplo, struct S {}; void f(S) {}podría compilarse en un archivo objeto, luego cambiarse la definición Sy struct S { int x; };usarse en la compilación de una llamada a f(S()). En tales casos, el compilador generalmente usará una convención de llamada diferente, pero en ambos casos fcambiará al mismo nombre, por lo que el vinculador no detectará este problema y el resultado generalmente será una falla o corrupción de datos o memoria en tiempo de ejecución. .

Ejemplos

C

Aunque la manipulación de nombres generalmente no es requerida ni utilizada por lenguajes que no soportan la sobrecarga de funciones , como C y Pascal clásico , la usan en algunos casos para proporcionar información adicional sobre una función. Por ejemplo, los compiladores destinados a plataformas Microsoft Windows admiten una variedad de convenciones de llamada , que determinan la manera 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 alteran los símbolos con códigos que detallan qué convención debe usarse para llamar a la rutina específica.

El esquema de manipulació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 se aplica incluso a otros lenguajes, como Pascal , D , Delphi , Fortran y C# . Esto permite que las subrutinas escritas en esos idiomas llamen o sean llamadas por bibliotecas existentes de Windows utilizando una convención de llamada diferente a la predeterminada.

Al compilar los siguientes ejemplos de C:

int _cdecl f ( int x ) { retorno 0 ; } int _stdcall g ( int y ) { return 0 ; } int _fastcall h ( int z ) { retorno 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 el número de bytes, en decimal, de los argumentos en la lista de parámetros (incluidos los pasados ​​en registros, para llamada rápida). 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. En algunos casos excepcionales, esta diferencia puede provocar problemas externos no resueltos al migrar dicho código a 64 bits. Por ejemplo, el código Fortran puede usar 'alias' para vincular un método C por nombre de la siguiente manera:

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

Esto compilará y vinculará bien en 32 bits, pero generará un externo sin resolver _fen 64 bits. Una solución para esto es no utilizar ningún 'alias' (en el que los nombres de los métodos normalmente deben estar en mayúscula en C y Fortran). Otra es utilizar la opción BIND:

SUBRUTINA f () BIND ( C , NOMBRE = "f" ) FINAL SUBRUTINA 

En C, la mayoría de los compiladores también modifican funciones y variables estáticas (y en C++ funciones y variables declaradas estáticas o colocadas en el espacio de nombres anónimo) en unidades de traducción utilizando las mismas reglas de manipulación que para sus versiones no estáticas. Si funciones con el mismo nombre (y parámetros para C++) también se definen y utilizan en diferentes unidades de traducción, también cambiarán al mismo nombre, lo que podría provocar un conflicto. Sin embargo, no serán equivalentes si se denominan en sus respectivas unidades de traducción. Los compiladores generalmente son libres de emitir modificaciones arbitrarias para estas funciones, porque es ilegal acceder a ellas directamente desde otras unidades de traducción, por lo que nunca necesitarán vincular diferentes códigos objeto (nunca es necesario vincularlos). Para evitar conflictos de vinculación, los compiladores utilizarán manipulació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 dependiendo de la unidad de traducción de donde proviene. Esto generalmente se hace mediante 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 del código fuente de C , que luego sería compilado por un compilador de C en código objeto; Debido a esto, los nombres de los símbolos tenían que ajustarse a las reglas de identificador 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 soportaba símbolos de C++ y todavía era necesario modificarlo.

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 eliminar de la ambigüedad modificando (decorando) el nombre de un símbolo . Debido a que los sistemas de manipulación de nombres para tales características no están estandarizados entre 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():

intf ( ) { retorno 1 ; } intf ( int ) { retorno 0 ; _ } vacío g () { int yo = f (), j = f ( 0 ); }                       

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

int __f_v () { retorno 1 ; } int __f_i ( int ) { retorno 0 ; } vacío __g_v () { int i = __f_v (), j = __f_i ( 0 ); }                        

Aunque su nombre es único, g()todavía está alterado: la alteración de nombres se aplica a todos los símbolos de C++ (excepto los de 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, de acuerdo con la ABI IA-64 (Itanium):

espacio de nombres wikipedia { artículo de clase { público : std :: formato de cadena (); // = _ZN9wikipedia7artículo6formatoEv            bool print_to ( std :: ostream & ); // = _ZN9wikipedia7article8print_toERSo    clase wikilink { público : wikilink ( std :: cadena constante y 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 nombres anidados (incluidos espacios de nombres y clases), esto va seguido de N, luego una serie de pares <longitud, id> (la longitud es la longitud del siguiente identificador) y, finalmente E, . Por ejemplo, wikipedia::article::formatse convierte en:

_ZN9wikipedia7artículo6formatoE

Para las funciones, esto va seguido de la información de tipo; como format()es una voidfunción, esto es simplemente v; por eso:

_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 manipulan las mismas funciones

No existe un esquema estandarizado mediante el cual incluso los identificadores triviales de C++ sean destrozados y, en consecuencia, diferentes compiladores (o incluso diferentes versiones del mismo compilador, o el mismo compilador en diferentes plataformas) destrocen los símbolos públicos de forma radicalmente diferente (y por lo tanto totalmente incompatible). maneras. Considere cómo diferentes compiladores de C++ manipulan las mismas funciones:

Notas:

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

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

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

es garantizar que los símbolos que contiene estén "destrozados": 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 no están alteradas, el compilador de C++ debe evitar alterar 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 } #endif

Así, código como:

if ( strcmp ( argv [ 1 ], "-x" ) == 0 ) strcpy ( a , argv [ 2 ]); else memset ( a , 0 , tamaño de ( a ));            

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

if ( __1cGstrcmp6Fpkc1_i_ ( argv [ 1 ], "-x" ) == 0 ) __1cGstrcpy6Fpcpkc_0_ ( a , argv [ 2 ]); else __1cGmemset6FpviI_0_ ( a , 0 , tamaño de ( 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 estandarizada de nombres en el lenguaje C++ conduciría a una mayor interoperabilidad entre las implementaciones del compilador. Sin embargo, tal 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 sólo uno de los varios detalles de la interfaz binaria de la aplicación (ABI) que una implementación de C++ debe decidir y observar. Otros aspectos de ABI, como el manejo de excepciones , el diseño de la tabla virtual , la estructura y el relleno del marco de la pila , también hacen que las diferentes implementaciones de C++ sean incompatibles. Además, requerir una forma particular de manipulación causaría problemas en 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 en la que 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 anotado de C++ (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 [6] se ha estandarizado la ABI de C++ completa, incluida la manipulación de nombres.

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

Debido a que los símbolos C++ se exportan rutinariamente desde DLL y archivos de objetos compartidos , el esquema de manipulación de nombres no es simplemente una cuestión interna del compilador. Diferentes compiladores (o diferentes versiones del mismo compilador, en muchos casos) producen dichos archivos binarios bajo diferentes esquemas de decoración de nombres, lo que significa que los símbolos frecuentemente no se resuelven si los compiladores utilizados para crear la biblioteca y el programa que la utiliza emplearon diferentes esquemas. Por ejemplo, si un sistema con múltiples compiladores de C++ instalados (por ejemplo, GNU GCC y el compilador del proveedor del sistema operativo) desea instalar las bibliotecas Boost C++ , tendría que compilarse varias veces (una para GCC y otra para el compilador del proveedor).

Es bueno por motivos de seguridad que los compiladores que producen códigos objeto incompatibles (códigos basados ​​en diferentes ABI, con respecto, por ejemplo, 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 oscuros y problemas graves de estabilidad).

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

Hay casos, particularmente en bases de código grandes y complejas, donde puede ser difícil o poco práctico asignar el nombre alterado emitido dentro de un mensaje de error del vinculador al token/nombre de variable correspondiente particular en la 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 pruebas, incluso si solo se utilizan un compilador y un vinculador. Los demanglers (incluidos aquellos dentro de los mecanismos de informe de errores del enlazador) a veces ayudan, pero el propio mecanismo de desmantelamiento puede descartar información desambiguadora crítica.

Demandar a través de c++filt

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

Demandar a través de GCC ABI incorporado

#incluye <stdio.h> #incluye <stdlib.h> #incluye <cxxabi.h>   int main () { const char * mangled_name = "_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_" ; estado entero = -1 ; char * demangled_name = abi :: __cxa_demangle ( mangled_name , NULL , NULL y estado ) ; printf ( "Demandado: %s \n " , nombre_demandado ); gratis ( nombre_demandado ); devolver 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 clase contiene su nombre y los tipos de argumentos de su 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 del archivo .class se diseñaron juntos (y tuvieron en mente la orientación a objetos y la interoperabilidad universal desde el principio).

Crear nombres únicos para clases internas y anónimas

El alcance de las clases anónimas se limita a su clase principal, 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 (interno 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 tiempo de ejecución). Entonces, compilando el siguiente programa Java:

public class foo { barra de clase { public int x ; }           public void zark () { Objeto f = nuevo Objeto () { public String toString () { return "hola" ; } }; } }                    

producirá tres archivos .class :

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

La resolución de nombres en Java es aún más complicada en tiempo de ejecución, ya que los nombres completos de las clases son únicos sólo 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 llamado cargador de clases de contexto, por lo que en los casos en que dos instancias diferentes de cargador de clases contienen clases con el mismo nombre, el sistema primero intenta cargar la clase usando el cargador de clases raíz (o sistema). y luego baja por la jerarquía hasta el cargador de clases de contexto.

Interfaz nativa de Java

Java Native Interface , el soporte de métodos nativos de Java, permite que los programas en lenguaje Java llamen a programas escritos en otro lenguaje (generalmente C o C++). Aquí existen 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 atributos de clase que no se desea que utilicen las subclases [8] y que se designan como tales dándoles un nombre con dos o más guiones bajos iniciales y no más de un guión bajo final. Por ejemplo, __thingserá destrozado, como lo será ___thingy __thing_, pero __thing__y __thing___no lo será. El tiempo de ejecución de Python no restringe el acceso a dichos atributos; la manipulación solo evita colisiones de nombres si una clase derivada define un atributo con el mismo nombre.

Al encontrar atributos de nombres alterados, Python transforma estos nombres anteponiendo un único guión bajo y el nombre de la clase adjunta, por ejemplo:

>>> clase  Prueba : ...  def  __mangled_name ( self ): ...  pasa ...  def  normal_name ( self ): ...  pasa >>> t  =  Prueba () >>> [ attr  para  attr  en  dir ( t )  si  "nombre"  en  attr ] ['_Test__mangled_name', 'normal_name']

Pascal

Turbo Pascal, Delfos

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

exporta el nombre de myFunc 'myFunc' , el nombre de myProc '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 a símbolos definidos en módulos externos creados con otro idioma y exportar sus propios símbolos para ser llamados por otro idioma. 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 manipulación de nombres también es necesaria en los compiladores de Fortran , originalmente porque el lenguaje no distingue entre mayúsculas y minúsculas . Más adelante en la evolución del lenguaje se impusieron más requisitos de manipulación debido a la adición de módulos y otras características en el estándar Fortran 90. La manipulación de casos, especialmente, es un problema común que debe abordarse para llamar a bibliotecas Fortran, como LAPACK , desde otros lenguajes, como C.

Debido a que no se distinguen entre mayúsculas y minúsculas, FOOel compilador debe convertir el nombre de una subrutina o función a un formato y caso estandarizados para que se vincule de la misma manera independientemente del caso. Diferentes compiladores han implementado esto de diversas maneras y no se ha producido ninguna estandarización. Los compiladores AIX y HP-UX Fortran convierten todos los identificadores a minúsculas foo, mientras que los compiladores Cray y Unicos Fortran convierten los identificadores a mayúsculas FOO. 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 Intel Fortran utiliza de forma predeterminada mayúsculas sin guión bajo. [9]foo_foo_bar_

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

el módulo m contiene la función entera cinco () cinco = 5 función final cinco módulo final m      

En este módulo, el nombre de la función se modificará como __m_MOD_five(p. ej., GNU Fortran), m_MP_five_(p. ej., ifort de Intel), m.five_(p. ej., sun95 de Oracle), etc. Dado que Fortran no permite sobrecargar el nombre de un procedimiento, sino que utiliza En cambio, bloques de interfaz genéricos y procedimientos genéricos vinculados a tipos, los nombres mutilados 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 están alterados de forma predeterminada en Rust . Sin embargo, esto puede desactivarse mediante el #[no_mangle]atributo de función. Este atributo se puede utilizar para exportar funciones a C, C++ u Objective-C . [11] Además, junto con el #[start]atributo de función o el #[no_main]atributo de caja, permite al usuario definir un punto de entrada estilo C para el programa. [12]

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

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

C objetivo

Esencialmente 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 la siguiente forma:

+ ( 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 usan -. Una declaración de método de clase típica podría verse así:

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

Con métodos de instancia parecidos a esto:

-  ( identificación )  valor ; -  ( identificación )  setValue: ( identificación ) nuevo_valor ;  

Cada una de estas declaraciones de métodos tiene una representación interna específica. Cuando se compila, cada método recibe un nombre de acuerdo con el siguiente esquema para métodos de clase:

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

y este por ejemplo métodos:

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

Los dos puntos en la sintaxis de Objective-C se traducen a guiones bajos. Entonces, 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 están etiquetados de esta forma. Sin embargo, buscar un método al que una clase pueda 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 número entero). Este símbolo se conoce como selector . En Objective-C, se pueden gestionar 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 selectores (a los que se les asigna un tipo SEL). Administrar selectores es más eficiente que manipular la representación de texto 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, las implementaciones de un método también reciben un identificador específico, estos se conocen como punteros de implementación y también reciben un tipo IMP.

El compilador codifica los envíos de mensajes como llamadas a la función, o a 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 selectores a sus implementaciones; el puntero de implementación especifica en qué memoria reside la implementación del método. Hay tablas separadas para métodos de clase y de instancia. Además de almacenarse 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. Esto permite el polimorfismo .

El tiempo de ejecución de Objective-C mantiene información sobre el argumento y los tipos 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 archivos binarios generados).

Rápido

Swift mantiene metadatos sobre funciones (y más) en los símbolos destrozados que se refieren 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 alterado 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: [16]

La manipulación de versiones desde Swift 4.0 está documentada oficialmente. Conserva cierta similitud con Itanium. [17]

Ver también

Referencias

  1. ^ "Interfaz binaria de la aplicación". Dlang.org . Consultado el 19 de mayo de 2020 .
  2. ^ Rainer, Schuetze (20 de diciembre de 2017). "La novedosa manipulación de nombres de D". El Blog D. Consultado el 19 de mayo de 2020 .
  3. ^ Clang - Características y objetivos: compatibilidad con GCC, 15 de abril de 2013
  4. ^ JBIntel_deleted_06032015. "Diferencias OBJ entre Intel Compiler y VC Compiler". software.intel.com .{{cite web}}: Mantenimiento CS1: nombres numéricos: lista de autores ( enlace )
  5. ^ "Compatibilidad con MSVC" . Consultado el 13 de mayo de 2016 .
  6. ^ "Itanium C++ ABI, sección 5.1 Nombres externos (también conocido como Mangling)" . Consultado el 16 de mayo de 2016 .
  7. ^ "Descripción general del diseño". docs.oracle.com .
  8. ^ "PEP 8 - Guía de estilo para código Python".
  9. ^ "Resumen de cuestiones de idiomas mixtos". Guía de referencia y de usuario para el compilador Intel Fortran 15.0 . Corporación Intel . Consultado el 17 de noviembre de 2014 .
  10. ^ "Biblioteca de documentación".
  11. ^ "Interfaz de función externa # Llamando al código Rust desde C". Manual de óxido . Rust-lang.org . Consultado el 13 de mayo de 2016 .
  12. ^ "Sin biblioteca estándar". Manual de óxido . Rust-lang.org . Consultado el 13 de mayo de 2016 .
  13. ^ "rust/src/librustc_codegen_utils/symbol_names/legacy.r.rs en 57e1da59cd0761330b4ea8d47b16340a78eeafa9 · rust-lang/rust · GitHub". GitHub . 3 de noviembre de 2021.
  14. ^ "Destrozo del símbolo de óxido". El libro RFC de Rust .
  15. ^ "Rust 1.42.0: src/test/ui/symbol-names". Github . Consultado el 20 de marzo de 2020 .
  16. ^ "mikeash.com: Preguntas y respuestas del viernes 15 de agosto de 2014: Cambio de nombres en Swift". mikeash.com .
  17. ^ "apple/swift: mangling.rst". GitHub . 3 de noviembre de 2021.

enlaces externos