From 8d188fa5c1f817034bbc398510213110059434cb Mon Sep 17 00:00:00 2001 From: Wagyourtail Date: Fri, 24 May 2024 04:46:39 -0500 Subject: [PATCH] rewrite downgrading classloader to not use a URLClassLoader --- .../jvmdg/gradle/task/DowngradeFiles.kt | 4 +- gradle.properties | 6 ++ .../jvmdg/coverage/ApiCoverageChecker.java | 2 +- .../wagyourtail/jvmdg/ClassDowngrader.java | 2 +- .../classloader/DowngradingClassLoader.java | 61 ++++++++++--------- .../jvmdg/classloader/FlatEnumeration.java | 38 ++++++++++++ .../jvmdg/classloader/ResourceProvider.java | 12 ++++ .../ClassLoaderResourceProvider.java | 35 +++++++++++ .../providers/FileSystemResourceProvider.java | 40 ++++++++++++ .../java/xyz/wagyourtail/jvmdg/cli/Main.java | 4 +- .../wagyourtail/jvmdg/compile/ApiShader.java | 8 +-- .../jvmdg/compile/ZipDowngrader.java | 6 +- .../xyz/wagyourtail/jvmdg/util/Utils.java | 13 ++-- .../jvmdg/internal/JvmDowngraderTest.java | 4 +- 14 files changed, 183 insertions(+), 52 deletions(-) create mode 100644 src/main/java/xyz/wagyourtail/jvmdg/classloader/FlatEnumeration.java create mode 100644 src/main/java/xyz/wagyourtail/jvmdg/classloader/ResourceProvider.java create mode 100644 src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/ClassLoaderResourceProvider.java create mode 100644 src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/FileSystemResourceProvider.java diff --git a/gradle-plugin/src/main/kotlin/xyz/wagyourtail/jvmdg/gradle/task/DowngradeFiles.kt b/gradle-plugin/src/main/kotlin/xyz/wagyourtail/jvmdg/gradle/task/DowngradeFiles.kt index 7d67e9fd..323dae72 100644 --- a/gradle-plugin/src/main/kotlin/xyz/wagyourtail/jvmdg/gradle/task/DowngradeFiles.kt +++ b/gradle-plugin/src/main/kotlin/xyz/wagyourtail/jvmdg/gradle/task/DowngradeFiles.kt @@ -77,7 +77,7 @@ abstract class DowngradeFiles : ConventionTask() { val downgraded = toDowngrade.map { temporaryDir.resolve(it.name) }.map { if (it.extension == "jar" || it.extension == "zip") { - val fs = Utils.openZipFileSystem(it.toPath(), mapOf("create" to "true")) + val fs = Utils.openZipFileSystem(it.toPath(), true) fileSystems.add(fs) fs.getPath("/") } else it.toPath() @@ -85,7 +85,7 @@ abstract class DowngradeFiles : ConventionTask() { toDowngrade = toDowngrade.map { if (it.isDirectory()) it else run { - val fs = Utils.openZipFileSystem(it, mapOf()) + val fs = Utils.openZipFileSystem(it, false) fileSystems.add(fs) fs.getPath("/") } diff --git a/gradle.properties b/gradle.properties index 691a4c27..4fc04830 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,13 +1,19 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx4G org.gradle.parallel=true + version=0.5.0 + asm_version=9.7 + mainVersion=7 testVersion=21 + # because java 7 support is in beta* testTargetVersion=8 + stubFromVersion=8 stubToVersion=22 + maven_group=xyz.wagyourtail.jvmdowngrader archives_base_name=jvmdowngrader diff --git a/java-api/src/coverage/java/xyz/wagyourtail/jvmdg/coverage/ApiCoverageChecker.java b/java-api/src/coverage/java/xyz/wagyourtail/jvmdg/coverage/ApiCoverageChecker.java index 44cd83b5..064ebe3b 100644 --- a/java-api/src/coverage/java/xyz/wagyourtail/jvmdg/coverage/ApiCoverageChecker.java +++ b/java-api/src/coverage/java/xyz/wagyourtail/jvmdg/coverage/ApiCoverageChecker.java @@ -50,7 +50,7 @@ public static void main(String[] args) throws IOException, URISyntaxException { Path home = Paths.get(sym.toURI()); - try (var fs = Utils.openZipFileSystem(home, new HashMap<>())) { + try (var fs = Utils.openZipFileSystem(home, false)) { System.out.println("Successfully opened \"" + home + "\" for coverage."); var versions = new HashMap>(); diff --git a/src/main/java/xyz/wagyourtail/jvmdg/ClassDowngrader.java b/src/main/java/xyz/wagyourtail/jvmdg/ClassDowngrader.java index e3e9724f..12c757f3 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/ClassDowngrader.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/ClassDowngrader.java @@ -45,7 +45,7 @@ protected ClassDowngrader(@NotNull Flags flags) { this.target = flags.classVersion; try { classLoader = new DowngradingClassLoader(this, ClassDowngrader.class.getClassLoader()); - } catch (MalformedURLException e) { + } catch (IOException e) { throw new RuntimeException(e); } downgraders = collectProviders(); diff --git a/src/main/java/xyz/wagyourtail/jvmdg/classloader/DowngradingClassLoader.java b/src/main/java/xyz/wagyourtail/jvmdg/classloader/DowngradingClassLoader.java index b5ac7cb4..61220074 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/classloader/DowngradingClassLoader.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/classloader/DowngradingClassLoader.java @@ -1,6 +1,8 @@ package xyz.wagyourtail.jvmdg.classloader; import xyz.wagyourtail.jvmdg.ClassDowngrader; +import xyz.wagyourtail.jvmdg.classloader.providers.ClassLoaderResourceProvider; +import xyz.wagyourtail.jvmdg.classloader.providers.FileSystemResourceProvider; import xyz.wagyourtail.jvmdg.util.Function; import xyz.wagyourtail.jvmdg.util.Utils; @@ -9,6 +11,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.FileSystem; import java.nio.file.Path; import java.util.*; import java.util.concurrent.atomic.AtomicReference; @@ -16,13 +19,13 @@ public class DowngradingClassLoader extends ClassLoader implements Closeable { private final ClassDowngrader holder; private final ClassDowngrader currentVersionDowngrader; - private final List delegates = new ArrayList<>(); + private final List delegates = new ArrayList<>(); - public DowngradingClassLoader(ClassDowngrader downgrader) throws MalformedURLException { + public DowngradingClassLoader(ClassDowngrader downgrader) throws IOException { super(); Path apiJar = downgrader.flags.findJavaApi(); if (apiJar != null) { - delegates.add(new URLClassLoader(new URL[]{apiJar.toUri().toURL()})); + delegates.add(new FileSystemResourceProvider(Utils.openZipFileSystem(apiJar, false))); } this.holder = downgrader; if (downgrader.target != Utils.getCurrentClassVersion()) { @@ -32,11 +35,11 @@ public DowngradingClassLoader(ClassDowngrader downgrader) throws MalformedURLExc } } - public DowngradingClassLoader(ClassDowngrader downgrader, ClassLoader parent) throws MalformedURLException { + public DowngradingClassLoader(ClassDowngrader downgrader, ClassLoader parent) throws IOException { super(parent); Path apiJar = downgrader.flags.findJavaApi(); if (apiJar != null) { - delegates.add(new URLClassLoader(new URL[]{apiJar.toUri().toURL()})); + delegates.add(new FileSystemResourceProvider(Utils.openZipFileSystem(apiJar, false))); } this.holder = downgrader; if (downgrader.target != Utils.getCurrentClassVersion()) { @@ -46,22 +49,26 @@ public DowngradingClassLoader(ClassDowngrader downgrader, ClassLoader parent) th } } - public DowngradingClassLoader(ClassDowngrader downgrader, URL[] urls, ClassLoader parent) throws MalformedURLException { + public DowngradingClassLoader(ClassDowngrader downgrader, List providers, ClassLoader parent) throws IOException { this(downgrader, parent); - delegates.add(new URLClassLoader(urls, getParent())); + delegates.addAll(providers); } - public DowngradingClassLoader(ClassDowngrader downgrader, URL[] urls) throws MalformedURLException { + public DowngradingClassLoader(ClassDowngrader downgrader, List providers) throws IOException { this(downgrader); - delegates.add(new URLClassLoader(urls, getParent())); + delegates.addAll(providers); } public void addDelegate(ClassLoader loader) { - delegates.add(loader); + delegates.add(new ClassLoaderResourceProvider(loader)); + } + + public void addDelegate(FileSystem jarFile) { + delegates.add(new FileSystemResourceProvider(jarFile)); } public void addDelegate(URL[] urls) { - delegates.add(new URLClassLoader(urls, getParent())); + delegates.add(new ClassLoaderResourceProvider(new URLClassLoader(urls))); } @Override @@ -126,25 +133,21 @@ public byte[] apply(String s) { @Override protected URL findResource(String name) { - for (ClassLoader delegate : delegates) { - URL resource = delegate.getResource(name); - if (resource != null) { - return resource; - } - } - return null; + return findResources(name).nextElement(); } @Override - protected Enumeration findResources(String name) throws IOException { - Vector vector = new Vector<>(); - for (ClassLoader delegate : delegates) { - Enumeration enumeration = delegate.getResources(name); - while (enumeration.hasMoreElements()) { - vector.add(enumeration.nextElement()); + protected Enumeration findResources(final String name) { + return new FlatEnumeration<>(Collections.enumeration(delegates), new Function>() { + @Override + public Enumeration apply(ResourceProvider resourceProvider) { + try { + return resourceProvider.getResources(name); + } catch (IOException e) { + throw new RuntimeException(e); + } } - } - return vector.elements(); + }); } @Override @@ -152,10 +155,8 @@ public void close() throws IOException { if (holder != currentVersionDowngrader) { currentVersionDowngrader.close(); } - for (ClassLoader delegate : delegates) { - if (delegate instanceof Closeable) { - ((Closeable) delegate).close(); - } + for (ResourceProvider delegate : delegates) { + delegate.close(); } } } diff --git a/src/main/java/xyz/wagyourtail/jvmdg/classloader/FlatEnumeration.java b/src/main/java/xyz/wagyourtail/jvmdg/classloader/FlatEnumeration.java new file mode 100644 index 00000000..c6e3ad1c --- /dev/null +++ b/src/main/java/xyz/wagyourtail/jvmdg/classloader/FlatEnumeration.java @@ -0,0 +1,38 @@ +package xyz.wagyourtail.jvmdg.classloader; + +import xyz.wagyourtail.jvmdg.util.Function; + +import java.util.Enumeration; + +public class FlatEnumeration implements Enumeration { + private final Enumeration enumeration; + private final Function> mapper; + + private Enumeration currentEnumeration = null; + + public FlatEnumeration(Enumeration enumeration, Function> mapper) { + this.enumeration = enumeration; + this.mapper = mapper; + } + + @Override + public boolean hasMoreElements() { + while (currentEnumeration == null || !currentEnumeration.hasMoreElements()) { + if (enumeration.hasMoreElements()) { + currentEnumeration = mapper.apply(enumeration.nextElement()); + } else { + return false; + } + } + return true; + } + + @Override + public E nextElement() { + if (!hasMoreElements()) { + return null; + } + return currentEnumeration.nextElement(); + } + +} diff --git a/src/main/java/xyz/wagyourtail/jvmdg/classloader/ResourceProvider.java b/src/main/java/xyz/wagyourtail/jvmdg/classloader/ResourceProvider.java new file mode 100644 index 00000000..1ce3268e --- /dev/null +++ b/src/main/java/xyz/wagyourtail/jvmdg/classloader/ResourceProvider.java @@ -0,0 +1,12 @@ +package xyz.wagyourtail.jvmdg.classloader; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +public interface ResourceProvider extends Closeable { + + Enumeration getResources(String name) throws IOException; + +} diff --git a/src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/ClassLoaderResourceProvider.java b/src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/ClassLoaderResourceProvider.java new file mode 100644 index 00000000..8c17fdc4 --- /dev/null +++ b/src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/ClassLoaderResourceProvider.java @@ -0,0 +1,35 @@ +package xyz.wagyourtail.jvmdg.classloader.providers; + +import xyz.wagyourtail.jvmdg.classloader.ResourceProvider; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +public class ClassLoaderResourceProvider implements ResourceProvider { + private final ClassLoader classLoader; + + public ClassLoaderResourceProvider(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public Enumeration getResources(String name) throws IOException { + return classLoader.getResources(name); + } + + @Override + public void close() throws IOException { + if (classLoader instanceof Closeable) { + ((Closeable) classLoader).close(); + } else if (classLoader instanceof AutoCloseable) { + try { + ((AutoCloseable) classLoader).close(); + } catch (Exception e) { + throw new IOException(e); + } + } + } + +} diff --git a/src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/FileSystemResourceProvider.java b/src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/FileSystemResourceProvider.java new file mode 100644 index 00000000..6fca7303 --- /dev/null +++ b/src/main/java/xyz/wagyourtail/jvmdg/classloader/providers/FileSystemResourceProvider.java @@ -0,0 +1,40 @@ +package xyz.wagyourtail.jvmdg.classloader.providers; + +import xyz.wagyourtail.jvmdg.classloader.ResourceProvider; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +public class FileSystemResourceProvider implements ResourceProvider { + private final FileSystem jarFile; + + public FileSystemResourceProvider(FileSystem jarFile) { + this.jarFile = jarFile; + } + + + @Override + public Enumeration getResources(String name) throws IOException { + List urls = new ArrayList<>(); + for (Path rootDirectory : jarFile.getRootDirectories()) { + Path resource = rootDirectory.resolve(name); + if (Files.exists(resource)) { + urls.add(resource.toUri().toURL()); + } + } + return Collections.enumeration(urls); + } + + @Override + public void close() throws IOException { + jarFile.close(); + } + +} diff --git a/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java b/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java index 453b4029..8182afc4 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/cli/Main.java @@ -160,13 +160,13 @@ public static void getTargets(Map> args, Map if (input.isDirectory()) { inputPath = input.toPath(); } else { - FileSystem fs = Utils.openZipFileSystem(input.toPath(), Collections.emptyMap()); + FileSystem fs = Utils.openZipFileSystem(input.toPath(), false); fileSystems.add(fs); inputPath = fs.getPath("/"); } File output = new File(target[1]); if (output.toString().endsWith(".jar") || output.toString().endsWith(".zip")) { - FileSystem fs = Utils.openZipFileSystem(output.toPath(), Collections.singletonMap("create", "true")); + FileSystem fs = Utils.openZipFileSystem(output.toPath(), true); fileSystems.add(fs); targets.put(inputPath, fs.getPath("/")); } else { diff --git a/src/main/java/xyz/wagyourtail/jvmdg/compile/ApiShader.java b/src/main/java/xyz/wagyourtail/jvmdg/compile/ApiShader.java index eb07426d..6bed8094 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/compile/ApiShader.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/compile/ApiShader.java @@ -59,9 +59,9 @@ public static void shadeApis(Flags flags, String prefix, File input, File output if (!prefix.endsWith("/")) { prefix += "/"; } - try (FileSystem apiFs = Utils.openZipFileSystem(resolveDowngradedApi(flags, downgradedApi), Collections.emptyMap())) { - try (FileSystem inputFs = Utils.openZipFileSystem(input.toPath(), Collections.emptyMap())) { - try (FileSystem outputFs = Utils.openZipFileSystem(output.toPath(), Collections.singletonMap("create", "true"))) { + try (FileSystem apiFs = Utils.openZipFileSystem(resolveDowngradedApi(flags, downgradedApi), false)) { + try (FileSystem inputFs = Utils.openZipFileSystem(input.toPath(), false)) { + try (FileSystem outputFs = Utils.openZipFileSystem(output.toPath(), true)) { Path apiRoot = apiFs.getPath("/"); Pair> api = scanApis(apiRoot); shadeApis(prefix, inputFs.getPath("/"), outputFs.getPath("/"), apiRoot, api.getFirst(), api.getSecond()); @@ -75,7 +75,7 @@ public static void shadeApis(Flags flags, String prefix, List inputRoots, prefix += "/"; } Path downgradedApiPath = resolveDowngradedApi(flags, downgradedApi); - try (FileSystem apiFs = Utils.openZipFileSystem(downgradedApiPath, Collections.emptyMap())) { + try (FileSystem apiFs = Utils.openZipFileSystem(downgradedApiPath,false)) { Pair> api = scanApis(apiFs.getPath("/")); for (int i = 0; i < inputRoots.size(); i++) { shadeApis(prefix, inputRoots.get(i), outputRoots.get(i), apiFs.getPath("/"), api.getFirst(), api.getSecond()); diff --git a/src/main/java/xyz/wagyourtail/jvmdg/compile/ZipDowngrader.java b/src/main/java/xyz/wagyourtail/jvmdg/compile/ZipDowngrader.java index 90ae23f1..5f1aac50 100644 --- a/src/main/java/xyz/wagyourtail/jvmdg/compile/ZipDowngrader.java +++ b/src/main/java/xyz/wagyourtail/jvmdg/compile/ZipDowngrader.java @@ -45,12 +45,10 @@ public static void downgradeZip(int opcVersion, Path input, Set classpath, } public static void downgradeZip(final ClassDowngrader downgrader, Path zip, Set classpath, final Path output) throws IOException { - try (final FileSystem zipfs = Utils.openZipFileSystem(zip, new HashMap())) { + try (final FileSystem zipfs = Utils.openZipFileSystem(zip, false)) { Files.createDirectories(output.getParent()); Files.deleteIfExists(output); - Map map = new HashMap<>(); - map.put("create", "true"); - try (final FileSystem outputZipFs = Utils.openZipFileSystem(output, map)) { + try (final FileSystem outputZipFs = Utils.openZipFileSystem(output, true)) { PathDowngrader.downgradePaths(downgrader, Collections.singletonList(zipfs.getPath("/")), Collections.singletonList(outputZipFs.getPath("/")), classpath); } } diff --git a/src/shared/java/xyz/wagyourtail/jvmdg/util/Utils.java b/src/shared/java/xyz/wagyourtail/jvmdg/util/Utils.java index 450c972e..a586d6a6 100644 --- a/src/shared/java/xyz/wagyourtail/jvmdg/util/Utils.java +++ b/src/shared/java/xyz/wagyourtail/jvmdg/util/Utils.java @@ -10,8 +10,9 @@ import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.Map; +import java.util.zip.ZipOutputStream; public class Utils { @@ -41,13 +42,11 @@ public static MethodHandles.Lookup getImplLookup() { throw new UnsupportedOperationException("Unable to get MethodHandles.Lookup.IMPL_LOOKUP"); } - public static FileSystem openZipFileSystem(Path path, Map options) throws IOException { - if (options.containsKey("create")) { - if (options.get("create") == Boolean.TRUE) { - options.put("create", "true"); - } + public static FileSystem openZipFileSystem(Path path, boolean create) throws IOException { + if (create && !Files.exists(path)) { + new ZipOutputStream(Files.newOutputStream(path)).close(); } - return FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), options, null); + return FileSystems.newFileSystem(path, null); } public static byte[] readAllBytes(InputStream in) throws IOException { diff --git a/src/test/java/xyz/wagyourtail/jvmdg/internal/JvmDowngraderTest.java b/src/test/java/xyz/wagyourtail/jvmdg/internal/JvmDowngraderTest.java index e89a9315..d051dfa5 100644 --- a/src/test/java/xyz/wagyourtail/jvmdg/internal/JvmDowngraderTest.java +++ b/src/test/java/xyz/wagyourtail/jvmdg/internal/JvmDowngraderTest.java @@ -22,7 +22,6 @@ public class JvmDowngraderTest { private static final Flags flags = new Flags(); private static final Properties props = new Properties(); - private static final Path javaApi = Path.of("./java-api/build/libs/jvmdowngrader-java-api-" + props.getProperty("version") + ".jar"); static { try (InputStream is = Files.newInputStream(Path.of("gradle.properties"))) { @@ -32,6 +31,9 @@ public class JvmDowngraderTest { } } + + private static final Path javaApi = Path.of("./java-api/build/libs/jvmdowngrader-java-api-" + props.getProperty("version") + ".jar"); + static { // System.setProperty("jvmdg.java-api", javaApi.toString()); flags.api = javaApi.toFile();