diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/build.gradle.kts | 39 | ||||
-rw-r--r-- | agent/src/main/java/moe/nea/modernjava/agent/Pack200Retransformer.java | 52 | ||||
-rw-r--r-- | agent/src/main/java/moe/nea/modernjava/agent/RelaunchEntryPoint.java | 70 |
3 files changed, 161 insertions, 0 deletions
diff --git a/agent/build.gradle.kts b/agent/build.gradle.kts new file mode 100644 index 0000000..983f917 --- /dev/null +++ b/agent/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + java + id("com.github.johnrengelman.shadow") version "7.1.2" +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(11)) +} + + +val shadowOnly: Configuration by configurations.creating { + attributes { + this.attribute(org.gradle.api.attributes.java.TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 11) + } +} + +dependencies { + shadowOnly(implementation("org.ow2.asm:asm:9.4")!!) + shadowOnly(implementation("org.ow2.asm:asm-commons:9.4")!!) + shadowOnly("dev.architectury:architectury-pack200:0.1.3") +} +tasks.withType(Jar::class) { + manifest { + attributes.apply { + this["Premain-Class"] = "moe.nea.modernjava.agent.Pack200Retransformer" + } + } +} + +tasks.shadowJar { + archiveClassifier.set("") + configurations = listOf(shadowOnly) + relocate("org.objectweb.asm", "moe.nea.modernjava.agent.dep.asm") + // relocate("dev.architectury.pack200.java", "java.util.jar") +} +tasks.jar { + archiveClassifier.set("thin") +} +tasks.assemble.get().dependsOn(tasks.shadowJar)
\ No newline at end of file diff --git a/agent/src/main/java/moe/nea/modernjava/agent/Pack200Retransformer.java b/agent/src/main/java/moe/nea/modernjava/agent/Pack200Retransformer.java new file mode 100644 index 0000000..67480bf --- /dev/null +++ b/agent/src/main/java/moe/nea/modernjava/agent/Pack200Retransformer.java @@ -0,0 +1,52 @@ +package moe.nea.modernjava.agent; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.List; + +/** + * Transforms all classes such that they use dev.architectury.pack200.java instead of java.util.jar for Pack200 packing + * and unpacking. We cannot simply transform the jar on the classpath, since defining a package in the java directory + * is impossible in most versions of java, even from the boot class path. In theory this could be a custom class loader, + * but the java agent is far more reliable. + */ +public class Pack200Retransformer implements ClassFileTransformer { + // relocate("dev.architectury.pack200.java", "java.util.jar") + List<String> classes = Arrays.asList("AdaptiveCoding", "Attribute", "BandStructure", "ClassReader", "ClassWriter", "Code", "Coding", "CodingChooser", "CodingMethod", "ConstantPool", "Constants", "Driver", "DriverResource", "DriverResource_ja", "DriverResource_zh_CN", "FixedList", "Fixups", "Histogram", "Instruction", "NativeUnpack", "Pack200", "Pack200Adapter", "Pack200Plugin", "Package", "PackageReader", "PackageWriter", "PackerImpl", "PopulationCoding", "PropMap", "TLGlobals", "UnpackerImpl", "Utils"); + String architecturyPackage = "dev/architectury/pack200/java/"; + String javaPackage = "java/util/jar/"; + + public static void premain(String agentArgs, Instrumentation inst) { + inst.addTransformer(new Pack200Retransformer()); + } + + @Override + public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + ClassReader reader = new ClassReader(classfileBuffer); + ClassWriter writer = new ClassWriter(reader, 0); + Remapper remapper = new Remapper() { + @Override + public String map(String internalName) { + if (internalName.startsWith(javaPackage)) { + for (String aClass : classes) { + if (internalName.equals(javaPackage + aClass) || (internalName.startsWith(javaPackage + aClass + "$"))) + return internalName.replace(javaPackage, architecturyPackage); + } + } + return internalName; + } + }; + ClassVisitor visitor = new ClassRemapper(writer, remapper); + reader.accept(visitor, 0); + return writer.toByteArray(); + } +} diff --git a/agent/src/main/java/moe/nea/modernjava/agent/RelaunchEntryPoint.java b/agent/src/main/java/moe/nea/modernjava/agent/RelaunchEntryPoint.java new file mode 100644 index 0000000..e39a677 --- /dev/null +++ b/agent/src/main/java/moe/nea/modernjava/agent/RelaunchEntryPoint.java @@ -0,0 +1,70 @@ +package moe.nea.modernjava.agent; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; + +/** + * The main class for the new java runtime. + */ +public class RelaunchEntryPoint { + /** + * Logs things with a prefix to indicate it's coming from the new java runtime. + */ + public static void log(String text) { + System.out.println("[MODERNJAVA] " + text); + + } + + /** + * Wrap {@link #setupLaunch(String[])} in a crash handler for logging. + */ + public static void main(String[] args) { + log("This is modern java relaunch talking!"); + try { + setupLaunch(args); + } catch (Throwable t) { + log("Encountered exception during relaunching"); + t.printStackTrace(); + } + } + + private static void setupLaunch(String[] args) throws Exception { + + // Check if old class loader can load java modules. + log("Original SQLException: " + Thread.currentThread().getContextClassLoader().loadClass("java.sql.SQLException")); + + log("Setting up fake launch class loader"); + ClassLoader urlClassLoader = new URLClassLoader(retrieveClassPath(), Thread.currentThread().getContextClassLoader()); + + log("Created fake url class loader"); + Thread.currentThread().setContextClassLoader(urlClassLoader); + log("Handing off to Launch"); + + // Check if new class loader can load java modules. + log("LaunchClass ClassLoader SQLException: " + urlClassLoader.loadClass("java.sql.SQLException")); + + // Load launch wrapper and check launch class loader + Class<?> launchClass = urlClassLoader.loadClass("net.minecraft.launchwrapper.Launch"); + log("LaunchClass: " + launchClass); + log("LaunchClass ClassLoader: " + launchClass.getClassLoader()); + + // Hand off to launch wrapper + Method main = launchClass.getMethod("main", String[].class); + main.invoke(null, new Object[]{args}); + } + + private static URL[] retrieveClassPath() throws MalformedURLException { + String property = System.getProperty("modernjava.relaunchclasspath"); + String[] split = property.split(File.pathSeparator); + URL[] urls = new URL[split.length]; + for (int i = 0; i < split.length; i++) { + urls[i] = new File(split[i]).toURI().toURL(); + } + log("Retrieved classpath: " + Arrays.toString(urls)); + return urls; + } +} |