¿Cómo saben los compiladores sobre otras clases y sus propiedades?

14

Estoy escribiendo mi primer lenguaje de programación que está orientado a objetos y hasta ahora es muy bueno para crear una sola 'clase'. Pero, digamos que quiero tener clases, digamos ClassAy ClassB. Siempre que estos dos no tengan nada que ver entre sí, entonces todo está bien. Sin embargo, digamos que ClassAcrea un ClassB--esto plantea 2 preguntas relacionadas:

-¿Cómo sabría el compilador cuando compila ClassAque ClassBincluso existe, y, si es así, cómo sabe sus propiedades?

Mis pensamientos hasta ahora habían sido: en lugar de compilar cada clase a la vez (es decir, escanear, analizar y generar código) cada "archivo (no realmente archivo, per se, sino una" clase ") necesito escanear + analizar cada uno primero , luego generar código para todos?

OnResolve
fuente

Respuestas:

13

Diferentes lenguajes (y, por lo tanto, compiladores) abordan esto de manera diferente.

En la familia C, los diferentes módulos tienen un archivo de encabezado correspondiente que se utiliza al construir el objeto. Los archivos de encabezado proporcionan información sobre el tamaño del objeto y qué funciones o métodos existen que pueden invocarse. Esto permite la información necesaria para la asignación de memoria y "¿existe ese método / función / procedimiento?" eso se usa al compilar una sola unidad que no necesita tener acceso a la fuente en sí.

En Java, el compilador es consciente de las cosas en su classpath e inspecciona esos objetos para vincularlos (verificando que existan métodos, que tengan el número correcto de argumentos, etc.). Java también puede vincularse dinámicamente durante la carga en tiempo de ejecución en otras clases de las que no sabe nada cuando se compiló. Consulte Class.forName para ver un ejemplo de carga dinámica.

Ambas opciones son bastante válidas y tienen su propio conjunto de ventajas y desventajas. Proporcionar archivos de encabezado que algunos ven como incómodo y violando DRY . Por otro lado, si no tiene archivos de encabezado, las bibliotecas deben ser inspeccionables por el compilador y el enlazador; un .so o .dll probablemente no tendrá suficiente información para instanciar adecuadamente los objetos o validar las llamadas al método ( y sería dependiente de la máquina).

Comunidad
fuente
0

En términos prácticos, con Java, el IDE analiza un programa completo a la vez; cuando se hace referencia a ClassB, el compilador IDE lo verá. Todo, incluidas las bibliotecas, es un todo completo. Una vez que el programa esté listo, puede cambiar las rutas de clase, intercambiar archivos .class individuales y cambiar las versiones de la biblioteca. También puede compilar archivos .java individuales sin usar el IDE (o de alguna manera evitar sus verificaciones). El resultado no necesita ser consistente en absoluto, y si no lo es , obtendrá excepciones de tiempo de ejecución. (Una de las muchas cosas que un IDE está tratando de hacer por usted es convertir los errores de tiempo de ejecución en tiempo de compilación, o más bien, errores de tiempo de edición).

C # es casi lo mismo, y no creo que C y C ++ sean realmente tan diferentes, ya que lo que hacen los IDE de Java y C # es simplemente crear encabezados de estilo C / C ++ para usted detrás de escena.

RalphChapin
fuente
Los compiladores de Java también compilan material dependiente, si es necesario. Solo obtiene excepciones de tiempo de ejecución de este tipo de cosas si realmente ha intentado forzar las cosas (por ejemplo, cambiar y recompilar una API después de compilar los consumidores de esa API).
Donal Fellows
@DonalFellows: Mis problemas han sido la falta de bibliotecas ("¡Pero puse esa en todas mis máquinas!") Y la recompilación de programas en ejecución (intercambio involuntario de archivos .class). Puedo prever la actualización de paquetes que ya no coinciden con el programa principal, aunque todavía no lo he hecho. Me hice hacer mucho de eso con C y de .dll (y lo había hecho a mí) hace años. Creo y espero que haya muchas protecciones vigentes ahora que no estaban allí entonces.
RalphChapin
Realmente no entiendo qué tiene que ver un IDE con cómo un compilador / enlazador sabe cómo resolver problemas de compilación / enlazador. Corrígeme si me equivoco, pero un IDE es totalmente ortogonal a todo el problema (excepto que facilita la tarea en el programador). ¿Por qué? Porque en teoría el IDE usa el compilador en segundo plano.
Thomas Eding
@ThomasEding: Tienes toda la razón. Cuando respondí a esta pregunta, parecía tener problemas para separar el IDE del compilador / enlazador. Mi excusa: Java no se "enlaza" hasta que se ejecuta y, por lo tanto, no puede saber que una referencia de clase o una llamada al método es incorrecta hasta entonces; el IDE realmente no "facilita" mi tarea, lo hace posible. Solía ​​(en FORTRAN y C, hace mucho tiempo) obtener errores cuando compilaba, cuando vinculaba y luego cuando corría. Ahora obtengo casi todos esos errores correctamente a medida que los escribo, lo que hace que el IDE sea un compilador, un vinculador y un ejecutor, en cierto sentido. Hoy, todos los errores que no son de tiempo de ejecución provienen del IDE.
RalphChapin
0

Los idiomas antiguos a veces son más estrictos; considere lo que es posible en Java:

public interface Ifc {
    public static final Ifc MY_CONSTANT = new Implem();
}

public class Implem implements Ifc {
}

He visto el antipatrón anterior, y es realmente feo (lo habría prohibido). Ambas unidades de compilación se utilizan entre sí. Pero Ifc se puede compilar en código sin tener un Implem compilado. El código compilado, .class, comparable a un C .obj, contiene "información de enlace:" una importación de Implem, que llama a un constructor sin parámetros Implem(). La clase Implem puede compilarse sin problemas. En parte, el ClassLoader: haciendo datos de clase de inicialización / creación de JVM, y en parte la propia máquina virtual Java, juega un poco como enlazador , integrando todo.

Por ejemplo, compilar con una versión de una biblioteca específica y ejecutar con otra versión de esa biblioteca reconocerá los errores de tiempo de ejecución.

Entonces la respuesta: la compilación entrega unidades de código de objeto compilado, que uno tiene que ver como código + datos + API para vincular entre sí.

El compilador debe luego también hacer un relleno de juntas, y verificar la API de vinculación; Una segunda fase.

Esto puede irritar y parecer poco elegante, pero las pruebas matemáticas pueden funcionar de la misma manera: al probar la corrección completa, uno ya puede considerar que una parte es verdadera hasta la verificación.

Joop Eggen
fuente