aboutsummaryrefslogtreecommitdiff
path: root/agent
diff options
context:
space:
mode:
authornea <nea@nea.moe>2023-11-01 18:50:11 +0100
committernea <nea@nea.moe>2023-11-01 18:50:11 +0100
commit297cb01f220a617dd08096467978b2fccbc27695 (patch)
treed467f358313ce3648063da41b66361a840fa7864 /agent
parentd5253dc5c3ae7b2cc1fcb96780e43b929a449eb4 (diff)
downloadModernJavaLauncher-297cb01f220a617dd08096467978b2fccbc27695.tar.gz
ModernJavaLauncher-297cb01f220a617dd08096467978b2fccbc27695.tar.bz2
ModernJavaLauncher-297cb01f220a617dd08096467978b2fccbc27695.zip
Add documentation
Diffstat (limited to 'agent')
-rw-r--r--agent/build.gradle.kts39
-rw-r--r--agent/src/main/java/moe/nea/modernjava/agent/Pack200Retransformer.java52
-rw-r--r--agent/src/main/java/moe/nea/modernjava/agent/RelaunchEntryPoint.java70
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;
+ }
+}