-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathBootstrapLauncherTransformer.java
77 lines (70 loc) · 4.01 KB
/
BootstrapLauncherTransformer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package me.earth.headlessmc.launcher.instrumentation.modlauncher;
import lombok.CustomLog;
import me.earth.headlessmc.launcher.instrumentation.AbstractClassTransformer;
import me.earth.headlessmc.launcher.instrumentation.EntryStream;
import me.earth.headlessmc.launcher.instrumentation.Target;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import java.util.Locale;
/**
* Modlauncher, BootstrapLauncher and SecureJarHandler all call ModuleLayer.boot sometimes.
* Normally this would be fine, but we cannot load forge from the boot layer,
* because we load it dynamically, at runtime.
* <p>So first, we replace all calls to ModuleLayer.boot with cpw.mods.cl.ModuleClassLoader.class.getModule().getLayer().
* <p>The second problem is that the ModuleClassloader delegates classloading of some classes
* to Classloader.getPlatformClassLoader, which we replace with cpw.mods.cl.ModuleClassLoader.class.getClassloader().
* <p>The layer and classloader of the ModuleClassLoader should be the one we introduced to load it.
*/
@CustomLog
public class BootstrapLauncherTransformer extends AbstractClassTransformer {
public BootstrapLauncherTransformer() {
super("<matches any class by overriding the match method>");
}
@Override
protected boolean matches(EntryStream stream) {
return stream.getEntry().getName().endsWith(".class")
&& !stream.getEntry().getName().endsWith("module-info.class");
}
@Override
public boolean matches(Target target) {
return target.getPath().toLowerCase(Locale.ENGLISH).contains("bootstraplauncher")
|| target.getPath().toLowerCase(Locale.ENGLISH).contains("modlauncher")
|| target.getPath().toLowerCase(Locale.ENGLISH).contains("securejarhandler");
}
@Override
protected void transform(ClassNode cn) {
// TOP TEN WORST IDEAS #8 THIS!
log.debug("Transforming " + cn.name);
for (MethodNode mn: cn.methods) {
for (AbstractInsnNode insn : mn.instructions) {
if (insn instanceof MethodInsnNode) {
//noinspection PatternVariableCanBeUsed
MethodInsnNode bootCall = (MethodInsnNode) insn;
if ("java/lang/ModuleLayer".equals(bootCall.owner)
&& "boot".equals(bootCall.name)
&& "()Ljava/lang/ModuleLayer;".equals(bootCall.desc)
&& bootCall.getOpcode() == Opcodes.INVOKESTATIC) {
log.debug("Found ModuleLayer.boot call in " + cn.name + "." + mn.name + mn.desc);
InsnList il = new InsnList();
il.add(new LdcInsnNode(Type.getType("Lcpw/mods/cl/ModuleClassLoader;")));
il.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class), "getModule", "()Ljava/lang/Module;"));
il.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Module", "getLayer", "()Ljava/lang/ModuleLayer;"));
mn.instructions.insert(insn, il);
mn.instructions.remove(insn);
} else if ("java/lang/ClassLoader".equals(bootCall.owner)
&& "getPlatformClassLoader".equals(bootCall.name)
&& "()Ljava/lang/ClassLoader;".equals(bootCall.desc)
&& bootCall.getOpcode() == Opcodes.INVOKESTATIC) {
log.debug("Found ClassLoader.getPlatformClassLoader call in " + cn.name + "." + mn.name + mn.desc);
InsnList il = new InsnList();
il.add(new LdcInsnNode(Type.getType("Lcpw/mods/cl/ModuleClassLoader;")));
il.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class), "getClassLoader", "()Ljava/lang/ClassLoader;"));
mn.instructions.insert(insn, il);
mn.instructions.remove(insn);
}
}
}
}
}
}