From 2c7c5a91f7617e9b4972dfebb9086a3e0c7e430a Mon Sep 17 00:00:00 2001 From: Wagyourtail Date: Fri, 1 Nov 2024 11:38:16 -0500 Subject: [PATCH 01/10] fix inflater/deflator --- gradle.properties | 2 +- .../jvmdg/j11/stub/java_base/J_U_Z_Deflater.java | 5 +++-- .../jvmdg/j11/stub/java_base/J_U_Z_Inflater.java | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 37c76d8d..729dc89f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx4G org.gradle.parallel=true -version=1.2.1 +version=1.2.2 asm_version=9.7.1 diff --git a/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Deflater.java b/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Deflater.java index 23240350..e04ef65f 100644 --- a/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Deflater.java +++ b/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Deflater.java @@ -20,8 +20,9 @@ public static int deflate(Deflater def, ByteBuffer buf, int flush) { @Stub public static void setInput(Deflater def, ByteBuffer buf) { - throw new UnsupportedOperationException( - "JVMDowngrader, setInput(ByteBuffer) is not supported because it's impure."); + byte[] remain = new byte[buf.remaining()]; + buf.get(remain); + def.setInput(remain); } } diff --git a/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Inflater.java b/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Inflater.java index 0985cabf..62f0d176 100644 --- a/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Inflater.java +++ b/java-api/src/java11/java/xyz/wagyourtail/jvmdg/j11/stub/java_base/J_U_Z_Inflater.java @@ -21,9 +21,10 @@ public static void setDictionary(Inflater inf, ByteBuffer buf) { } @Stub - public static void setInput(Deflater def, ByteBuffer buf) { - throw new UnsupportedOperationException( - "JVMDowngrader, setInput(ByteBuffer) is not supported because it's impure."); + public static void setInput(Inflater inf, ByteBuffer buf) { + byte[] remain = new byte[buf.remaining()]; + buf.get(remain); + inf.setInput(remain); } } From 292c26012f484d80f05abbe5dced7348007393b8 Mon Sep 17 00:00:00 2001 From: William Gray Date: Tue, 19 Nov 2024 09:03:50 -0600 Subject: [PATCH 02/10] Update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bd279c1f..e5e97583 100644 --- a/README.md +++ b/README.md @@ -180,13 +180,14 @@ I do not automatically make the configuration shadowed into your output. #### Including newer dependencies on lower java version building -This is not recommended due to the following but is still possible. -It is recommended to just shade dependencies into your project and downgrade the combined output. +It is **strongly** not recommended due to the following but is still possible. +instead, you *should* just shade dependencies into your project and downgrade the combined output. The dependencies may not be correctly represented in the pom with this method. -There may be issues with gradle metadata breaking because of how early gradle checks the java version, -you can disable this by setting `mavenPom` and `artifact` in the `metadataSources` function on repositories -to explicitly disable gradle metadata. +There may be issues with metadata breaking because of how early gradle checks the java version, +you can disable this by setting `artifact` in the `metadataSources` function on repositories +to explicitly disable metadata, if you are lucky, you may be able to include `mavenPom` as well, so trasitive dependencies can resolve, otherwise +**transitive dependencies will not be included**, and must be included manually. for example: @@ -194,7 +195,7 @@ for example: repositories { mavenCentral { metadataSources { - mavenPom() + mavenPom() // may still break with this line on some dependencies artifact() } } From d1377f197787ac0eb9a4c9df96f7fcda24f51256 Mon Sep 17 00:00:00 2001 From: Rhys <98863820+rhysdh540@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:28:15 -0500 Subject: [PATCH 03/10] add javac plugin and test --- build.gradle.kts | 4 + java-api/build.gradle.kts | 1 + javac-plugin/build.gradle.kts | 37 ++++ .../jvmdg/javac/JvmdgJavacPlugin.java | 171 ++++++++++++++++++ .../services/com.sun.source.util.Plugin | 1 + javac-plugin/src/test/java/TheTest.java | 18 ++ settings.gradle.kts | 1 + 7 files changed, 233 insertions(+) create mode 100644 javac-plugin/build.gradle.kts create mode 100644 javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java create mode 100644 javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin create mode 100644 javac-plugin/src/test/java/TheTest.java diff --git a/build.gradle.kts b/build.gradle.kts index 759324fc..0e69dd8d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -148,6 +148,8 @@ java { tasks.jar { from(sourceSets["main"].output, sourceSets["shared"].output) + from(project("javac-plugin").sourceSets["main"].output) + from(rootDir.resolve("LICENSE.md")) from(rootDir.resolve("license")) { into("license") @@ -171,6 +173,7 @@ tasks.jar { tasks.getByName("sourcesJar") { from(sourceSets["shared"].allSource) + from(project("javac-plugin").sourceSets["main"].allSource) from(rootDir.resolve("LGPLv2.1.md")) isPreserveFileTimestamps = false @@ -185,6 +188,7 @@ project.evaluationDependsOnChildren() val shadowJar by tasks.registering(ShadowJar::class) { from(sourceSets["main"].output, sourceSets["shared"].output) + from(project("javac-plugin").sourceSets["main"].output) from(rootDir.resolve("LGPLv2.1.md")) isPreserveFileTimestamps = false diff --git a/java-api/build.gradle.kts b/java-api/build.gradle.kts index 04e31698..15b81ef4 100644 --- a/java-api/build.gradle.kts +++ b/java-api/build.gradle.kts @@ -134,6 +134,7 @@ tasks.getByName("compileCoverageJava") { val genCtSym by tasks.registering(GenerateCtSymTask::class) { group = "jvmdg" + lowerVersion = fromVersion upperVersion = toVersion - 1 } diff --git a/javac-plugin/build.gradle.kts b/javac-plugin/build.gradle.kts new file mode 100644 index 00000000..13475aeb --- /dev/null +++ b/javac-plugin/build.gradle.kts @@ -0,0 +1,37 @@ +import org.gradle.internal.os.OperatingSystem + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } +} + +dependencies { + compileOnly(rootProject.sourceSets["main"].output) + compileOnly(rootProject.sourceSets["shared"].output) + + // macos doesn't link tools.jar by default for some reason + val javaHome = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(8)) }.get().metadata.installationPath + implementation(files("$javaHome/lib/tools.jar")) + + testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks.compileTestJava { + classpath += rootProject.tasks["shadowJar"].outputs.files + + javaCompiler = javaToolchains.compilerFor { + languageVersion.set(JavaLanguageVersion.of(17)) + } + + options.compilerArgs.add("-Xplugin:jvmdg target=8 log=info") +} + +tasks.test { + useJUnitPlatform() + + javaLauncher = javaToolchains.launcherFor { + languageVersion.set(JavaLanguageVersion.of(8)) + } +} \ No newline at end of file diff --git a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java new file mode 100644 index 00000000..ae4187ab --- /dev/null +++ b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java @@ -0,0 +1,171 @@ +package xyz.wagyourtail.jvmdg.javac; + +import com.sun.source.util.Plugin; +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.BasicJavacTask; +import com.sun.tools.javac.main.JavaCompiler; +import xyz.wagyourtail.jvmdg.ClassDowngrader; +import xyz.wagyourtail.jvmdg.cli.Flags; +import xyz.wagyourtail.jvmdg.compile.PathDowngrader; +import xyz.wagyourtail.jvmdg.logging.Logger; +import xyz.wagyourtail.jvmdg.util.Utils; + +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; +import java.io.Closeable; +import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.*; + +@SuppressWarnings("UrlHashCode") +public class JvmdgJavacPlugin implements Plugin, Closeable { + private BasicJavacTask task; + private Flags flags; + private final Set classpathURLs = new HashSet<>(); + + @Override + public String getName() { + return "jvmdg"; + } + + static { + int vmVersion = Utils.classVersionToMajorVersion(Utils.getCurrentClassVersion()); + if(vmVersion > 8) { + try { + Method getModule = Class.class.getDeclaredMethod("getModule"); + openModule(getModule.invoke(JavacTask.class)); + } catch (Throwable t) { + Utils.sneakyThrow(t); + } + } + } + + @Override + public void init(JavacTask t, String... args) { + this.task = (BasicJavacTask) t; + + JavaCompiler compiler = JavaCompiler.instance(task.getContext()); + compiler.closeables = compiler.closeables.prepend(this); + + this.flags = new Flags(); + flags.findJavaApi(); + +// final String p = String.format("[%s]", File.separator); + final String p = "[" + File.separator + "]"; + + for(String arg : args) { + if(arg.contains("=")) { + String[] split = arg.split("=", 2); + switch(split[0]) { + case "api": + if(flags.api == null) { + flags.api = new ArrayList<>(); + } + for(String s : split[1].split(p)) { + flags.api.add(new File(s)); + } + break; + case "logLevel": + case "log": + flags.logLevel = Logger.Level.valueOf(split[1].toUpperCase(Locale.ROOT)); + break; + case "skipStubs": + for(String s : split[1].split(",")) { + flags.debugSkipStubs.add(Integer.parseInt(s)); + } + break; + case "target": + flags.classVersion = Utils.majorVersionToClassVersion(Integer.parseInt(split[1])); + break; + case "classpath": + case "cp": + for(String s : split[1].split(p)) { + try { + classpathURLs.add(new File(s).toURI().toURL()); + } catch (Throwable t1) { + Utils.sneakyThrow(t1); + } + } + break; + } + } + } + } + + @Override + public void close() { + try { + runDowngrade(); + } catch (Throwable t) { + Utils.sneakyThrow(t); + } + } + + private void runDowngrade() throws Throwable { + final JavaFileManager fileManager = task.getContext().get(JavaFileManager.class); + + File root = new File( + fileManager.getJavaFileForOutput( + StandardLocation.CLASS_OUTPUT, + "", JavaFileObject.Kind.CLASS, null + ).toUri() + ).getParentFile(); + + // must be mutable, since GradleStandardJavaFileManager calls remove + Set kindSet = new HashSet<>(); + kindSet.add(JavaFileObject.Kind.CLASS); + + for(JavaFileObject jfo : fileManager.list(StandardLocation.CLASS_PATH, "", kindSet, true)) { + classpathURLs.add(jfo.toUri().toURL()); + } + + Path tempOutput = Files.createTempDirectory("downgrade"); + tempOutput.toFile().deleteOnExit(); + + try(ClassDowngrader cd = ClassDowngrader.downgradeTo(flags)) { + cd.logger.debug("classpath: " + classpathURLs); + + PathDowngrader.downgradePaths( + cd, + Collections.singletonList(root.toPath()), + Collections.singletonList(tempOutput), + classpathURLs + ); + } + + Files.walk(tempOutput) + .filter(Files::isRegularFile) + .forEach(p -> { + try { + Path target = root.toPath().resolve(tempOutput.relativize(p)); + Files.createDirectories(target.getParent()); + Files.move(p, target, StandardCopyOption.REPLACE_EXISTING); + } catch (Throwable t) { + Utils.sneakyThrow(t); + } + }); + } + + public static void openModule(Object o) throws Throwable { + Class moduleClass = o.getClass(); + if(!moduleClass.getName().equals("java.lang.Module")) { + throw new IllegalArgumentException("Not a module: " + o); + } + MethodHandle implAddOpens = Utils.getImplLookup().findVirtual(moduleClass, "implAddOpens", + MethodType.methodType(void.class, String.class)); + + @SuppressWarnings("unchecked") + Set packages = (Set) moduleClass.getDeclaredMethod("getPackages").invoke(o); + + for(String pn : packages) { + implAddOpens.invoke(o, pn); + } + } +} diff --git a/javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin b/javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin new file mode 100644 index 00000000..a5d5daae --- /dev/null +++ b/javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin @@ -0,0 +1 @@ +xyz.wagyourtail.jvmdg.javac.JvmdgJavacPlugin \ No newline at end of file diff --git a/javac-plugin/src/test/java/TheTest.java b/javac-plugin/src/test/java/TheTest.java new file mode 100644 index 00000000..f6840963 --- /dev/null +++ b/javac-plugin/src/test/java/TheTest.java @@ -0,0 +1,18 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TheTest { + @Test + public void test() { + // if this class loads, the plugin is working + + // just make sure we're running on Java 8 + assertTrue(System.getProperty("java.version").startsWith("1.8")); + + String h = """ + Hello World! + """; + assertEquals("Hello World!\n", h); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 693f49f5..ec70fc04 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,7 @@ plugins { include("gradle-plugin") include("java-api") include("site") +include("javac-plugin") include("testing") include("testing:downgrade") From 2167dd1219ce57569d7fea7fd72bb2664550459c Mon Sep 17 00:00:00 2001 From: Rhys <98863820+rhysdh540@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:27:51 +0700 Subject: [PATCH 04/10] remove old commented code --- .../java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java index ae4187ab..af8ebfbb 100644 --- a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java +++ b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java @@ -55,9 +55,8 @@ public void init(JavacTask t, String... args) { compiler.closeables = compiler.closeables.prepend(this); this.flags = new Flags(); - flags.findJavaApi(); + flags.api = flags.findJavaApi(); -// final String p = String.format("[%s]", File.separator); final String p = "[" + File.separator + "]"; for(String arg : args) { From 6f651f9232c8994df3b693df0f133ba681f93c8a Mon Sep 17 00:00:00 2001 From: Rhys <98863820+rhysdh540@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:13:45 -0500 Subject: [PATCH 05/10] use argument parsing from `Main`, misc. cleanup --- javac-plugin/build.gradle.kts | 14 ++--- .../jvmdg/javac/JvmdgJavacPlugin.java | 59 ++++--------------- .../java/xyz/wagyourtail/jvmdg/cli/Main.java | 4 ++ 3 files changed, 19 insertions(+), 58 deletions(-) diff --git a/javac-plugin/build.gradle.kts b/javac-plugin/build.gradle.kts index 13475aeb..403b2a24 100644 --- a/javac-plugin/build.gradle.kts +++ b/javac-plugin/build.gradle.kts @@ -1,18 +1,16 @@ -import org.gradle.internal.os.OperatingSystem - java { toolchain { languageVersion.set(JavaLanguageVersion.of(8)) } } +val java8 = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(8)) }.get() + dependencies { compileOnly(rootProject.sourceSets["main"].output) compileOnly(rootProject.sourceSets["shared"].output) - // macos doesn't link tools.jar by default for some reason - val javaHome = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(8)) }.get().metadata.installationPath - implementation(files("$javaHome/lib/tools.jar")) + implementation(files("${java8.metadata.installationPath}/lib/tools.jar")) testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") testRuntimeOnly("org.junit.platform:junit-platform-launcher") @@ -25,13 +23,9 @@ tasks.compileTestJava { languageVersion.set(JavaLanguageVersion.of(17)) } - options.compilerArgs.add("-Xplugin:jvmdg target=8 log=info") + options.compilerArgs.add("-Xplugin:jvmdg --classVersion 52 --logLevel info") } tasks.test { useJUnitPlatform() - - javaLauncher = javaToolchains.launcherFor { - languageVersion.set(JavaLanguageVersion.of(8)) - } } \ No newline at end of file diff --git a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java index af8ebfbb..35f24160 100644 --- a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java +++ b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java @@ -6,8 +6,8 @@ import com.sun.tools.javac.main.JavaCompiler; import xyz.wagyourtail.jvmdg.ClassDowngrader; import xyz.wagyourtail.jvmdg.cli.Flags; +import xyz.wagyourtail.jvmdg.cli.Main; import xyz.wagyourtail.jvmdg.compile.PathDowngrader; -import xyz.wagyourtail.jvmdg.logging.Logger; import xyz.wagyourtail.jvmdg.util.Utils; import javax.tools.JavaFileManager; @@ -15,6 +15,7 @@ import javax.tools.StandardLocation; import java.io.Closeable; import java.io.File; +import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.reflect.Method; @@ -39,6 +40,7 @@ public String getName() { int vmVersion = Utils.classVersionToMajorVersion(Utils.getCurrentClassVersion()); if(vmVersion > 8) { try { + //noinspection JavaReflectionMemberAccess Method getModule = Class.class.getDeclaredMethod("getModule"); openModule(getModule.invoke(JavacTask.class)); } catch (Throwable t) { @@ -55,59 +57,20 @@ public void init(JavacTask t, String... args) { compiler.closeables = compiler.closeables.prepend(this); this.flags = new Flags(); - flags.api = flags.findJavaApi(); - - final String p = "[" + File.separator + "]"; - - for(String arg : args) { - if(arg.contains("=")) { - String[] split = arg.split("=", 2); - switch(split[0]) { - case "api": - if(flags.api == null) { - flags.api = new ArrayList<>(); - } - for(String s : split[1].split(p)) { - flags.api.add(new File(s)); - } - break; - case "logLevel": - case "log": - flags.logLevel = Logger.Level.valueOf(split[1].toUpperCase(Locale.ROOT)); - break; - case "skipStubs": - for(String s : split[1].split(",")) { - flags.debugSkipStubs.add(Integer.parseInt(s)); - } - break; - case "target": - flags.classVersion = Utils.majorVersionToClassVersion(Integer.parseInt(split[1])); - break; - case "classpath": - case "cp": - for(String s : split[1].split(p)) { - try { - classpathURLs.add(new File(s).toURI().toURL()); - } catch (Throwable t1) { - Utils.sneakyThrow(t1); - } - } - break; - } - } + + try { + Main.parseArgs(args, flags); + } catch (Throwable e) { + Utils.sneakyThrow(e); } } @Override - public void close() { - try { - runDowngrade(); - } catch (Throwable t) { - Utils.sneakyThrow(t); - } + public void close() throws IOException { + runDowngrade(); } - private void runDowngrade() throws Throwable { + private void runDowngrade() throws IOException { final JavaFileManager fileManager = task.getContext().get(JavaFileManager.class); File root = new File( diff --git a/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java b/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java index dda3ad3c..0de0d93b 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java @@ -23,6 +23,10 @@ public class Main { private static Deque tempFiles = new ArrayDeque<>(); public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + parseArgs(args, flags); + } + + public static void parseArgs(String[] args, Flags flags) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { Arguments parser = new Arguments("JvmDowngrader", null, null, null); Arguments input = new Arguments("--target", "input to output\n (required)\n you can use - for a temp-file if you want to chain operations", new String[]{"-t"}, new String[]{"input jar|path", "output jar|path"}); Arguments classpath = new Arguments("--classpath", "Classpath to use\n (highly recommended)", new String[]{"-cp"}, new String[]{"classpath"}); From ebcebd946cd9c8e2aae9144edbd26913057d5c01 Mon Sep 17 00:00:00 2001 From: Rhys <98863820+rhysdh540@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:02:46 -0500 Subject: [PATCH 06/10] more cleanup, don't rerun coverageReport all the time --- javac-plugin/build.gradle.kts | 3 ++- .../xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/javac-plugin/build.gradle.kts b/javac-plugin/build.gradle.kts index 403b2a24..fde80434 100644 --- a/javac-plugin/build.gradle.kts +++ b/javac-plugin/build.gradle.kts @@ -17,7 +17,8 @@ dependencies { } tasks.compileTestJava { - classpath += rootProject.tasks["shadowJar"].outputs.files + classpath += project(":java-api").tasks.getByName("testJar").outputs.files + + files(rootProject.sourceSets.map { it.compileClasspath }.flatten()) javaCompiler = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(17)) diff --git a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java index 35f24160..6fda37f6 100644 --- a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java +++ b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java @@ -25,11 +25,9 @@ import java.nio.file.StandardCopyOption; import java.util.*; -@SuppressWarnings("UrlHashCode") public class JvmdgJavacPlugin implements Plugin, Closeable { private BasicJavacTask task; private Flags flags; - private final Set classpathURLs = new HashSet<>(); @Override public String getName() { @@ -70,6 +68,7 @@ public void close() throws IOException { runDowngrade(); } + @SuppressWarnings("UrlHashCode") private void runDowngrade() throws IOException { final JavaFileManager fileManager = task.getContext().get(JavaFileManager.class); @@ -80,11 +79,11 @@ private void runDowngrade() throws IOException { ).toUri() ).getParentFile(); - // must be mutable, since GradleStandardJavaFileManager calls remove - Set kindSet = new HashSet<>(); - kindSet.add(JavaFileObject.Kind.CLASS); + Set classpathURLs = new HashSet<>(); - for(JavaFileObject jfo : fileManager.list(StandardLocation.CLASS_PATH, "", kindSet, true)) { + // the set argument must be mutable, since GradleStandardJavaFileManager calls remove + for(JavaFileObject jfo : fileManager.list(StandardLocation.CLASS_PATH, "", + new HashSet<>(Collections.singletonList(JavaFileObject.Kind.CLASS)), true)) { classpathURLs.add(jfo.toUri().toURL()); } From 4ea96f43e9a571167c1e9500e85c226ce9f95912 Mon Sep 17 00:00:00 2001 From: Rhys <98863820+rhysdh540@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:11:22 -0500 Subject: [PATCH 07/10] depend correctly --- javac-plugin/build.gradle.kts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/javac-plugin/build.gradle.kts b/javac-plugin/build.gradle.kts index fde80434..404427eb 100644 --- a/javac-plugin/build.gradle.kts +++ b/javac-plugin/build.gradle.kts @@ -14,12 +14,13 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + testAnnotationProcessor(sourceSets["main"].output) + testAnnotationProcessor(project.project(":java-api").tasks.getByName("testJar").outputs.files) + testAnnotationProcessor(rootProject.sourceSets["main"].runtimeClasspath) } tasks.compileTestJava { - classpath += project(":java-api").tasks.getByName("testJar").outputs.files + - files(rootProject.sourceSets.map { it.compileClasspath }.flatten()) - javaCompiler = javaToolchains.compilerFor { languageVersion.set(JavaLanguageVersion.of(17)) } From 7f81a5eeb1145061032232bbaf0d84e55fee989f Mon Sep 17 00:00:00 2001 From: Rhys <98863820+rhysdh540@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:17:02 -0500 Subject: [PATCH 08/10] depend correctly v2 --- javac-plugin/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javac-plugin/build.gradle.kts b/javac-plugin/build.gradle.kts index 404427eb..e1c23ddc 100644 --- a/javac-plugin/build.gradle.kts +++ b/javac-plugin/build.gradle.kts @@ -16,7 +16,6 @@ dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher") testAnnotationProcessor(sourceSets["main"].output) - testAnnotationProcessor(project.project(":java-api").tasks.getByName("testJar").outputs.files) testAnnotationProcessor(rootProject.sourceSets["main"].runtimeClasspath) } @@ -25,7 +24,8 @@ tasks.compileTestJava { languageVersion.set(JavaLanguageVersion.of(17)) } - options.compilerArgs.add("-Xplugin:jvmdg --classVersion 52 --logLevel info") + val apiJar = project(":java-api").tasks.named("testJar").get().outputs.files.singleFile + options.compilerArgs.add("-Xplugin:jvmdg --classVersion 52 --logLevel info --api ${apiJar.absolutePath}") } tasks.test { From 41463a691af7f4da0893ff97fd60506b8c825ce5 Mon Sep 17 00:00:00 2001 From: Wagyourtail Date: Wed, 4 Dec 2024 02:35:35 -0600 Subject: [PATCH 09/10] allow shading in javac plugin by making downgrade/shade args work --- javac-plugin/build.gradle.kts | 2 +- .../jvmdg/javac/JvmdgJavacPlugin.java | 124 +++++++++++------- javac-plugin/src/test/java/TheTest.java | 2 + .../xyz/wagyourtail/jvmdg/cli/Arguments.java | 13 ++ .../java/xyz/wagyourtail/jvmdg/cli/Main.java | 29 ++-- 5 files changed, 113 insertions(+), 57 deletions(-) diff --git a/javac-plugin/build.gradle.kts b/javac-plugin/build.gradle.kts index e1c23ddc..285971b3 100644 --- a/javac-plugin/build.gradle.kts +++ b/javac-plugin/build.gradle.kts @@ -25,7 +25,7 @@ tasks.compileTestJava { } val apiJar = project(":java-api").tasks.named("testJar").get().outputs.files.singleFile - options.compilerArgs.add("-Xplugin:jvmdg --classVersion 52 --logLevel info --api ${apiJar.absolutePath}") + options.compilerArgs.add("-Xplugin:jvmdg downgrade shade --prefix test --classVersion 52 --logLevel info --api ${apiJar.absolutePath}") } tasks.test { diff --git a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java index 6fda37f6..1cb01537 100644 --- a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java +++ b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java @@ -5,9 +5,11 @@ import com.sun.tools.javac.api.BasicJavacTask; import com.sun.tools.javac.main.JavaCompiler; import xyz.wagyourtail.jvmdg.ClassDowngrader; -import xyz.wagyourtail.jvmdg.cli.Flags; +import xyz.wagyourtail.jvmdg.Constants; +import xyz.wagyourtail.jvmdg.cli.Arguments; import xyz.wagyourtail.jvmdg.cli.Main; import xyz.wagyourtail.jvmdg.compile.PathDowngrader; +import xyz.wagyourtail.jvmdg.util.Lazy; import xyz.wagyourtail.jvmdg.util.Utils; import javax.tools.JavaFileManager; @@ -19,15 +21,25 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.reflect.Method; +import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.*; +import java.util.stream.Stream; -public class JvmdgJavacPlugin implements Plugin, Closeable { +public class JvmdgJavacPlugin extends Main implements Plugin, Closeable { private BasicJavacTask task; - private Flags flags; + private String[] args; + + private final Lazy fileManager = new Lazy() { + @Override + protected JavaFileManager init() { + return task.getContext().get(JavaFileManager.class); + } + }; @Override public String getName() { @@ -47,6 +59,30 @@ public String getName() { } } + @Override + protected Arguments buildArgumentList() { + Arguments args = super.buildArgumentList(); + Arguments downgrade = args.getChild("downgrade"); + Arguments target = downgrade.getChild("--target"); + // remove --target + downgrade.removeChild(target); + args.getChild("shade").removeChild(target); + return args; + } + + @Override + public void getTargets(Map> args, Map targets, List fileSystems) throws IOException { + if (!Constants.DIR.exists() && !Constants.DIR.mkdirs()) { + throw new IOException("Failed to create directory: " + Constants.DIR); + } + Path files = Files.createTempDirectory(Constants.DIR.toPath(), "downgrade").toAbsolutePath(); + System.out.println(files); + files.toFile().deleteOnExit(); + + targets.put(tempFiles.poll().toPath(), files); + tempFiles.push(files.toFile()); + } + @Override public void init(JavacTask t, String... args) { this.task = (BasicJavacTask) t; @@ -54,64 +90,64 @@ public void init(JavacTask t, String... args) { JavaCompiler compiler = JavaCompiler.instance(task.getContext()); compiler.closeables = compiler.closeables.prepend(this); - this.flags = new Flags(); - - try { - Main.parseArgs(args, flags); - } catch (Throwable e) { - Utils.sneakyThrow(e); - } + this.args = args; } @Override public void close() throws IOException { - runDowngrade(); + execute(); } + @Override @SuppressWarnings("UrlHashCode") - private void runDowngrade() throws IOException { - final JavaFileManager fileManager = task.getContext().get(JavaFileManager.class); + public Set getClasspath(Map> args) throws MalformedURLException { + Set classpathURLs = new HashSet<>(); + + try { + // the set argument must be mutable, since GradleStandardJavaFileManager calls remove + for (JavaFileObject jfo : fileManager.get().list(StandardLocation.CLASS_PATH, "", + new HashSet<>(Collections.singletonList(JavaFileObject.Kind.CLASS)), true)) { + classpathURLs.add(jfo.toUri().toURL()); + } + } catch (IOException e) { + Utils.sneakyThrow(e); + } + + return classpathURLs; + } + private void execute() throws IOException { File root = new File( - fileManager.getJavaFileForOutput( + fileManager.get().getJavaFileForOutput( StandardLocation.CLASS_OUTPUT, "", JavaFileObject.Kind.CLASS, null ).toUri() ).getParentFile(); - Set classpathURLs = new HashSet<>(); - - // the set argument must be mutable, since GradleStandardJavaFileManager calls remove - for(JavaFileObject jfo : fileManager.list(StandardLocation.CLASS_PATH, "", - new HashSet<>(Collections.singletonList(JavaFileObject.Kind.CLASS)), true)) { - classpathURLs.add(jfo.toUri().toURL()); - } - - Path tempOutput = Files.createTempDirectory("downgrade"); - tempOutput.toFile().deleteOnExit(); + tempFiles.add(root); - try(ClassDowngrader cd = ClassDowngrader.downgradeTo(flags)) { - cd.logger.debug("classpath: " + classpathURLs); - - PathDowngrader.downgradePaths( - cd, - Collections.singletonList(root.toPath()), - Collections.singletonList(tempOutput), - classpathURLs - ); + try { + parseArgs(args); + } catch (Exception e) { + Utils.sneakyThrow(e); } - Files.walk(tempOutput) - .filter(Files::isRegularFile) - .forEach(p -> { - try { - Path target = root.toPath().resolve(tempOutput.relativize(p)); - Files.createDirectories(target.getParent()); - Files.move(p, target, StandardCopyOption.REPLACE_EXISTING); - } catch (Throwable t) { - Utils.sneakyThrow(t); - } - }); + File output = tempFiles.pollFirst(); + + assert output != null; + if (output.equals(root)) return; + + Stream walk = Files.walk(output.toPath()); + walk.filter(Files::isRegularFile) + .forEach(p -> { + try { + Path target = root.toPath().resolve(output.toPath().relativize(p)); + Files.createDirectories(target.getParent()); + Files.move(p, target, StandardCopyOption.REPLACE_EXISTING); + } catch (Throwable t) { + Utils.sneakyThrow(t); + } + }); } public static void openModule(Object o) throws Throwable { diff --git a/javac-plugin/src/test/java/TheTest.java b/javac-plugin/src/test/java/TheTest.java index f6840963..60e68f1a 100644 --- a/javac-plugin/src/test/java/TheTest.java +++ b/javac-plugin/src/test/java/TheTest.java @@ -10,6 +10,8 @@ public void test() { // just make sure we're running on Java 8 assertTrue(System.getProperty("java.version").startsWith("1.8")); + "test %s".formatted("test2"); + String h = """ Hello World! """; diff --git a/src/main/java/xyz/wagyourtail/jvmdg/cli/Arguments.java b/src/main/java/xyz/wagyourtail/jvmdg/cli/Arguments.java index 644c057f..ce0688d8 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/cli/Arguments.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/cli/Arguments.java @@ -46,6 +46,19 @@ public Arguments addChildren(Arguments... children) { return this; } + public Arguments getChild(String arg) { + for (Set strings : this.children.keySet()) { + if (strings.contains(arg.toLowerCase(Locale.ROOT))) { + return children.get(strings); + } + } + return null; + } + + public Arguments removeChild(Arguments child) { + return this.children.remove(child.altNames); + } + public String help() { StringBuilder ret = new StringBuilder(); for (Arguments child : children.values()) { diff --git a/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java b/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java index 0de0d93b..8262c45f 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java @@ -18,15 +18,14 @@ import java.util.*; public class Main { - private static final Flags flags = new Flags(); - - private static Deque tempFiles = new ArrayDeque<>(); + protected final Flags flags = new Flags(); + protected final Deque tempFiles = new ArrayDeque<>(); public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { - parseArgs(args, flags); + new Main().parseArgs(args); } - public static void parseArgs(String[] args, Flags flags) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + protected Arguments buildArgumentList() { Arguments parser = new Arguments("JvmDowngrader", null, null, null); Arguments input = new Arguments("--target", "input to output\n (required)\n you can use - for a temp-file if you want to chain operations", new String[]{"-t"}, new String[]{"input jar|path", "output jar|path"}); Arguments classpath = new Arguments("--classpath", "Classpath to use\n (highly recommended)", new String[]{"-cp"}, new String[]{"classpath"}); @@ -61,6 +60,11 @@ public static void parseArgs(String[] args, Flags flags) throws IOException, Cla new Arguments("", "Arguments for main run", new String[]{}, new String[]{"args..."}) ) ); + return parser; + } + + public void parseArgs(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + Arguments parser = buildArgumentList(); List argList = new ArrayList<>(Arrays.asList(args)); Map> parsed = parser.read(argList, false); @@ -180,7 +184,7 @@ public static void parseArgs(String[] args, Flags flags) throws IOException, Cla } - public static void debug(Map> args) throws IOException { + public void debug(Map> args) throws IOException { for (Map.Entry> entry : args.entrySet()) { switch (entry.getKey()) { case "--print": @@ -215,7 +219,7 @@ public static void debug(Map> args) throws IOException { } } - public static void getTargets(Map> args, Map targets, List fileSystems) throws IOException { + public void getTargets(Map> args, Map targets, List fileSystems) throws IOException { for (Map.Entry> entry : args.entrySet()) { //noinspection SwitchStatementWithTooFewBranches switch (entry.getKey()) { @@ -249,7 +253,8 @@ public static void getTargets(Map> args, Map if (!Constants.DIR.exists() && !Constants.DIR.mkdirs()) { throw new IOException("Failed to create directory: " + Constants.DIR); } - output = File.createTempFile(input.getName(), ".jar", Constants.DIR); + output = File.createTempFile(input.getName(), ".jar", Constants.DIR).getAbsoluteFile(); + output.deleteOnExit(); tempFiles.add(output); } else { output = new File(target[1]); @@ -276,7 +281,7 @@ public static void getTargets(Map> args, Map } } - public static Set getClasspath(Map> args) throws MalformedURLException { + public Set getClasspath(Map> args) throws MalformedURLException { Set classpath = new HashSet<>(); if (args.containsKey("--classpath")) { for (String[] s : args.get("--classpath")) { @@ -290,7 +295,7 @@ public static Set getClasspath(Map> args) throws Mal return classpath; } - public static void downgrade(Map> args) throws IOException { + public void downgrade(Map> args) throws IOException { Map targets = new HashMap<>(); List fileSystems = new ArrayList<>(); try { @@ -313,7 +318,7 @@ public static void downgrade(Map> args) throws IOExceptio } } - public static void shade(Map> args) throws IOException { + public void shade(Map> args) throws IOException { if (!args.containsKey("--prefix")) { throw new IllegalArgumentException("No prefix specified"); } @@ -362,7 +367,7 @@ public static void shade(Map> args) throws IOException { } } - public static void bootstrap(Map> args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + public void bootstrap(Map> args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { if (!args.containsKey("--main")) { throw new IllegalArgumentException("No main class specified"); } From 675313ee2a5816bb8cb92c653b65078bb6465ec7 Mon Sep 17 00:00:00 2001 From: Wagyourtail Date: Wed, 4 Dec 2024 02:38:33 -0600 Subject: [PATCH 10/10] remove args that shouldn't be usable from javac --- .../main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java index 1cb01537..2618312e 100644 --- a/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java +++ b/javac-plugin/src/main/java/xyz/wagyourtail/jvmdg/javac/JvmdgJavacPlugin.java @@ -66,7 +66,9 @@ protected Arguments buildArgumentList() { Arguments target = downgrade.getChild("--target"); // remove --target downgrade.removeChild(target); + downgrade.removeChild(downgrade.getChild("--classpath")); args.getChild("shade").removeChild(target); + args.removeChild(args.getChild("bootstrap")); return args; }