Java-TX Classloader berücksichtigt Klassen von JVM Classpath #327

Closed
opened 2024-04-27 11:33:33 +00:00 by i21023 · 0 comments
Collaborator

Ich habe versucht herauszufinden, warum meine Java-TX Quelldateien compilieren, obwohl Abhängigkeiten zu .java Quelldateien bestehen und keine kompilierten .class-Dateien der .java Dateien im von mir beim Compileraufrufs angegebenen classpath sind.

Ich versuche meine Erkenntnisse hier möglichst ausführlich zu schildern. Den aktuell in Java implementierten Compiler nenne ich hier "Java-TX Compiler in Java".

Beschreibung des Problems

Als Beispiel habe ich die Klasse Assumption.jav verwendet, da sie relativ simple ist und genau eine Abhängigkeit zu einer Klasse (de.dhbwstuttgart.syntaxtree.TypeScope) besitzt, die bislang noch als .java Datei vorliegt.

//Assumption.jav

package de.dhbwstuttgart.typeinference.assumptions;
import de.dhbwstuttgart.syntaxtree.TypeScope;


public class Assumption {
    private final TypeScope typeScope;

    public Assumption (typeScope) {
        this.typeScope = typeScope;
    }

    public getTypeScope() {
        return typeScope;
    }
}

Ich möchte nun ich die Datei Assumption.jav compilieren.
Dazu habe ich den JavaTXCompilerCore in Intellij geöffnet und folgendes als Argument übergeben, um besser debuggen zu können, was passiert.

-d
"/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/out"
-cp
"/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/javatx-src"
"/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/javatx-src/main/java/de/dhbwstuttgart/typeinference/assumptions/Assumption.jav"

Wie man sieht, gebe ich als Classpath lediglich das Root Verzeichnis meiner Packethierarchie der Quelldateien an. Hier liegen generell nur die Quelldateien (überwiegend .java Dateien und einige .jav Dateien, die schon migriert wurde) und keine Classfiles. Zusätzlich gebe ich ein separates Output Verzeichnis an, welches vom Compiler auch dem Classpath hinzugefügt wird. Dieses ist aber zu diesem Zeitpunkt komplett leer. Meiner Meinung nach sollte die Datei also nicht compilieren, weil keine Classfile der Klasse TypeScope im von mir angegebenen Classpath liegt.

Allerdings compiliert die Datei korrekt.

Ursache

Ich denke das Problem hängt damit zusammen wie der Classloader implementiert ist. Wenn ich den "JavaTX-Compiler in Java" ausführen möchte, benötigt die JVM natürlich Zugriff auf sämtliche Classfiles sowie Maven Libraries des Compilers. Daher startet Intellij die JVM mit einigen Verzeichnissen im Classpath. Der Classpath mit dem der Java-TX Compiler in Java gestartet wird, habe ich hier mal rein kopiert.

/home/julian/Programmierung/uni/Studienarbeit/JavaCompilerCore/target/classes:/home/julian/.m2/repository/org/antlr/antlr4/4.11.1/antlr4-4.11.1.jar:/home/julian/.m2/repository/org/antlr/antlr4-runtime/4.11.1/antlr4-runtime-4.11.1.jar:/home/julian/.m2/repository/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar:/home/julian/.m2/repository/org/antlr/ST4/4.3.4/ST4-4.3.4.jar:/home/julian/.m2/repository/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar:/home/julian/.m2/repository/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar:/home/julian/.m2/repository/com/ibm/icu/icu4j/71.1/icu4j-71.1.jar:/home/julian/.m2/repository/commons-io/commons-io/2.6/commons-io-2.6.jar:/home/julian/.m2/repository/com/google/guava/guava/22.0/guava-22.0.jar:/home/julian/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar:/home/julian/.m2/repository/com/google/errorprone/error_prone_annotations/2.0.18/error_prone_annotations-2.0.18.jar:/home/julian/.m2/repository/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar:/home/julian/.m2/repository/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar:/home/julian/.m2/repository/org/reflections/reflections/0.9.11/reflections-0.9.11.jar:/home/julian/.m2/repository/org/javassist/javassist/3.21.0-GA/javassist-3.21.0-GA.jar:/home/julian/.m2/repository/org/ow2/asm/asm/9.5/asm-9.5.jar

Das meiste davon sind wie gesagt die ganzen Maven Libraries, allerdings ist der erste Pfad spannend. Hier wird das automatisch generierte target/classes directory angegeben. Hier liegen alle Classfiles des aktuellen Java-TX Compilers in Java.

Und natürlich braucht der "Java-TX Compiler in Java" diese Dateien auch um zu funktionieren, hier steht ja gerade der Code drin.
Soweit also alles korrekt.

Das Problem ist nur, dass der "Java-TX Compiler in Java" nicht nur die explizit per -cp beim Aufruf des Compilers angegebenen Pfade durchsucht, sondern die Suche an den nächst höheren Classloader (ApplicationClassLoader) weitergibt. Und vermutlich ist das auch korrekt, man will ja schließlich auch Klassen von z.B. java.lang oder java.util in Java-TX importieren, die in höheren Classloadern geladen werden.

grafik
Hier eine kleine Grafik zur Funktionsweise des Classloaders

Der Compiler startet also damit im eigens überschriebenen ClassLoader DirectoryClassLoader nach der Klasse TypeScope zu suchen. Hier durchsucht er die Pfade "/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/javatx-src" und
"/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/out", welche ja von mir mittels -cp beim Aufruf des Compilers angegeben wurde, nach der Klasse.
Er findet sie hier natürlich nicht.

Das Problem ist jetzt, dass er die Suche an den ApplicationClassLoader weitergibt. Dieser sucht dann nach der Klasse im an die JVM übergebenen classpath, also der in diesem Fall beim Starten Intellij übergeben wurde (das ist die große Liste an Pfaden von oben). Das Problem ist natürlich, dass er die Classfiles mit genau der gesuchten Packethierarchie hier findet. Der "Java-TX Compiler in Java" und "Java-TX Compiler in Java-TX" haben ja schließlich die gleichen Dateien/Aufbau. Das ist ein Problem. Der Java-TX Compiler durchsucht den Classpath der zum Ausführen des Compilers selbst notwenig ist, nach Classfiles, die im Quellcode, den der Compiler compilieren soll, verwendet werden. Das sollte so ziemlich sicher nicht sein.

Es kann ja z.B. sein, dass die Classfile schon auf einem ganz anderen Stand als die .java Datei im "Java-TX-Compiler in Java-TX" ist.

Außerdem kann ich in jeder X-beliebigen Datei einfach sämtlichen Klassen vom Compiler importieren.

Beispiel:

import de.dhbwstuttgart.syntaxtree.TypeScope;

class Foo{}

$ java -jar JavaTXcompiler-2.5-jar-with-dependencies.jar Foo.jav

Diese Datei liegt auf einer ganz anderen Stelle auf meinem Rechner alleine herum und wird hier mit der jar Datei aufgerufen. Hier besteht das gleiche Problem natürlich auch, nur dass die Classfiles und Libraries direkt im Jar-Archiv liegen. de.dhbwstuttgart.syntaxtree.TypeScope sollte hier nicht verfügbar sein. Aber der Code compiliert 😄

Man müsste also vermutlich irgendwie den ApplicationClassLoader überspringen, um zu verhindern, dass Dateien aus dem Classpath mit dem die JVM gestartet wurde, durchsucht werden. Die Classloader, die nach dem ApplicationClassLoader kommen, müssen aber wahrscheinlich wieder durchsucht werden, damit weiterhin Klassen aus z.B. java.lang importiert werden können.

Ich habe versucht herauszufinden, warum meine Java-TX Quelldateien compilieren, obwohl Abhängigkeiten zu .java Quelldateien bestehen und keine kompilierten .class-Dateien der .java Dateien im von mir beim Compileraufrufs angegebenen classpath sind. Ich versuche meine Erkenntnisse hier möglichst ausführlich zu schildern. Den aktuell in Java implementierten Compiler nenne ich hier "Java-TX Compiler in Java". ### Beschreibung des Problems Als Beispiel habe ich die Klasse Assumption.jav verwendet, da sie relativ simple ist und genau eine Abhängigkeit zu einer Klasse (de.dhbwstuttgart.syntaxtree.TypeScope) besitzt, die bislang noch als .java Datei vorliegt. ```java //Assumption.jav package de.dhbwstuttgart.typeinference.assumptions; import de.dhbwstuttgart.syntaxtree.TypeScope; public class Assumption { private final TypeScope typeScope; public Assumption (typeScope) { this.typeScope = typeScope; } public getTypeScope() { return typeScope; } } ``` Ich möchte nun ich die Datei Assumption.jav compilieren. Dazu habe ich den JavaTXCompilerCore in Intellij geöffnet und folgendes als Argument übergeben, um besser debuggen zu können, was passiert. ``` -d "/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/out" -cp "/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/javatx-src" "/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/javatx-src/main/java/de/dhbwstuttgart/typeinference/assumptions/Assumption.jav" ``` Wie man sieht, gebe ich als Classpath lediglich das Root Verzeichnis meiner Packethierarchie der Quelldateien an. Hier liegen generell nur die Quelldateien (überwiegend .java Dateien und einige .jav Dateien, die schon migriert wurde) und keine Classfiles. Zusätzlich gebe ich ein separates Output Verzeichnis an, welches vom Compiler auch dem Classpath hinzugefügt wird. Dieses ist aber zu diesem Zeitpunkt komplett leer. Meiner Meinung nach sollte die Datei also nicht compilieren, weil keine Classfile der Klasse TypeScope im von mir angegebenen Classpath liegt. Allerdings compiliert die Datei korrekt. ### Ursache Ich denke das Problem hängt damit zusammen wie der Classloader implementiert ist. Wenn ich den "JavaTX-Compiler in Java" ausführen möchte, benötigt die JVM natürlich Zugriff auf sämtliche Classfiles sowie Maven Libraries des Compilers. Daher startet Intellij die JVM mit einigen Verzeichnissen im Classpath. Der Classpath mit dem der `Java-TX Compiler in Java` gestartet wird, habe ich hier mal rein kopiert. `/home/julian/Programmierung/uni/Studienarbeit/JavaCompilerCore/target/classes:/home/julian/.m2/repository/org/antlr/antlr4/4.11.1/antlr4-4.11.1.jar:/home/julian/.m2/repository/org/antlr/antlr4-runtime/4.11.1/antlr4-runtime-4.11.1.jar:/home/julian/.m2/repository/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar:/home/julian/.m2/repository/org/antlr/ST4/4.3.4/ST4-4.3.4.jar:/home/julian/.m2/repository/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar:/home/julian/.m2/repository/org/glassfish/javax.json/1.1.4/javax.json-1.1.4.jar:/home/julian/.m2/repository/com/ibm/icu/icu4j/71.1/icu4j-71.1.jar:/home/julian/.m2/repository/commons-io/commons-io/2.6/commons-io-2.6.jar:/home/julian/.m2/repository/com/google/guava/guava/22.0/guava-22.0.jar:/home/julian/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar:/home/julian/.m2/repository/com/google/errorprone/error_prone_annotations/2.0.18/error_prone_annotations-2.0.18.jar:/home/julian/.m2/repository/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar:/home/julian/.m2/repository/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar:/home/julian/.m2/repository/org/reflections/reflections/0.9.11/reflections-0.9.11.jar:/home/julian/.m2/repository/org/javassist/javassist/3.21.0-GA/javassist-3.21.0-GA.jar:/home/julian/.m2/repository/org/ow2/asm/asm/9.5/asm-9.5.jar` Das meiste davon sind wie gesagt die ganzen Maven Libraries, allerdings ist der erste Pfad spannend. Hier wird das automatisch generierte target/classes directory angegeben. Hier liegen alle Classfiles des aktuellen `Java-TX Compilers in Java`. Und natürlich braucht der "Java-TX Compiler in Java" diese Dateien auch um zu funktionieren, hier steht ja gerade der Code drin. Soweit also alles korrekt. Das Problem ist nur, dass der "Java-TX Compiler in Java" nicht nur die explizit per -cp beim Aufruf des Compilers angegebenen Pfade durchsucht, sondern die Suche an den nächst höheren Classloader (ApplicationClassLoader) weitergibt. Und vermutlich ist das auch korrekt, man will ja schließlich auch Klassen von z.B. java.lang oder java.util in Java-TX importieren, die in höheren Classloadern geladen werden. ![grafik](/attachments/edfea063-27fa-428f-bb46-1676176857af) Hier eine kleine Grafik zur Funktionsweise des Classloaders Der Compiler startet also damit im eigens überschriebenen ClassLoader DirectoryClassLoader nach der Klasse TypeScope zu suchen. Hier durchsucht er die Pfade "/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/javatx-src" und "/home/julian/Programmierung/uni/Studienarbeit/JavaTXCompilerInJavaTX/out", welche ja von mir mittels -cp beim Aufruf des Compilers angegeben wurde, nach der Klasse. Er findet sie hier natürlich nicht. Das Problem ist jetzt, dass er die Suche an den ApplicationClassLoader weitergibt. Dieser sucht dann nach der Klasse im an die JVM übergebenen classpath, also der in diesem Fall beim Starten Intellij übergeben wurde (das ist die große Liste an Pfaden von oben). Das Problem ist natürlich, dass er die Classfiles mit genau der gesuchten Packethierarchie hier findet. Der "Java-TX Compiler in Java" und "Java-TX Compiler in Java-TX" haben ja schließlich die gleichen Dateien/Aufbau. Das ist ein Problem. Der Java-TX Compiler durchsucht den Classpath der zum Ausführen des Compilers selbst notwenig ist, nach Classfiles, die im Quellcode, den der Compiler compilieren soll, verwendet werden. Das sollte so ziemlich sicher nicht sein. Es kann ja z.B. sein, dass die Classfile schon auf einem ganz anderen Stand als die .java Datei im "Java-TX-Compiler in Java-TX" ist. Außerdem kann ich in jeder X-beliebigen Datei einfach sämtlichen Klassen vom Compiler importieren. **Beispiel:** ```java import de.dhbwstuttgart.syntaxtree.TypeScope; class Foo{} ``` `$ java -jar JavaTXcompiler-2.5-jar-with-dependencies.jar Foo.jav ` Diese Datei liegt auf einer ganz anderen Stelle auf meinem Rechner alleine herum und wird hier mit der jar Datei aufgerufen. Hier besteht das gleiche Problem natürlich auch, nur dass die Classfiles und Libraries direkt im Jar-Archiv liegen. `de.dhbwstuttgart.syntaxtree.TypeScope` sollte hier nicht verfügbar sein. Aber der Code compiliert 😄 Man müsste also vermutlich irgendwie den ApplicationClassLoader überspringen, um zu verhindern, dass Dateien aus dem Classpath mit dem die JVM gestartet wurde, durchsucht werden. Die Classloader, die nach dem ApplicationClassLoader kommen, müssen aber wahrscheinlich wieder durchsucht werden, damit weiterhin Klassen aus z.B. `java.lang` importiert werden können.
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: JavaTX/JavaCompilerCore#327
No description provided.