Java-TX Classloader berücksichtigt Klassen von JVM Classpath #327
Labels
No Label
Codegen
confirmed
duplicate
Eclipse-Plugin
Feature Request
generics
in progress
invalid
JavaCompilerCore
needs info
Parser
Trash
Type
Unify
won't fix
works for me
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: JavaTX/JavaCompilerCore#327
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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.
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.
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.
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 -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.