Skip to content

Commit 3d3cabc

Browse files
committed
Prevent FML from loading two ZipFileSystems for every mod jar
1 parent d691407 commit 3d3cabc

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

main/src/main/java/org/embeddedt/blacksmith/impl/TransformerCore.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class TransformerCore {
4040
TRANSFORMERS.add(new ModuleLayerHandlerTransformer());
4141
TRANSFORMERS.add(new FileWatcherTransformer());
4242
TRANSFORMERS.add(new BackgroundScanHandlerTransformer());
43+
TRANSFORMERS.add(new TransformerDiscovererConstantsTransformer());
4344
}
4445

4546
public static void log(String s) {

main/src/main/java/org/embeddedt/blacksmith/impl/transformers/RuntimeTransformer.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package org.embeddedt.blacksmith.impl.transformers;
22

33
import org.objectweb.asm.Opcodes;
4+
import org.objectweb.asm.Type;
45
import org.objectweb.asm.tree.AbstractInsnNode;
56
import org.objectweb.asm.tree.ClassNode;
67
import org.objectweb.asm.tree.InsnList;
8+
import org.objectweb.asm.tree.LocalVariableNode;
79
import org.objectweb.asm.tree.MethodInsnNode;
10+
import org.objectweb.asm.tree.MethodNode;
811

912
import java.lang.instrument.IllegalClassFormatException;
1013
import java.util.List;
14+
import java.util.stream.Stream;
15+
import java.util.stream.StreamSupport;
1116

1217
public interface RuntimeTransformer {
1318
String HOOK_CLASS = "org/embeddedt/blacksmith/impl/hooks/Hooks";
@@ -25,4 +30,22 @@ static <T extends AbstractInsnNode> T swapInstruction(InsnList list, AbstractIns
2530
list.set(oldInsn, newInsn);
2631
return newInsn;
2732
}
33+
34+
static int nextLocalVariableIndex(MethodNode method) {
35+
int maxIndex = (method.access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
36+
37+
for (LocalVariableNode var : method.localVariables) {
38+
int varSize = Type.getType(var.desc).getSize();
39+
int endIndex = var.index + varSize;
40+
if (endIndex > maxIndex) {
41+
maxIndex = endIndex;
42+
}
43+
}
44+
45+
return maxIndex;
46+
}
47+
48+
static Stream<AbstractInsnNode> streamInsnList(InsnList list) {
49+
return StreamSupport.stream(list.spliterator(), false);
50+
}
2851
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.embeddedt.blacksmith.impl.transformers;
2+
3+
import org.objectweb.asm.ClassWriter;
4+
import org.objectweb.asm.Opcodes;
5+
import org.objectweb.asm.Type;
6+
import org.objectweb.asm.tree.AbstractInsnNode;
7+
import org.objectweb.asm.tree.ClassNode;
8+
import org.objectweb.asm.tree.InsnList;
9+
import org.objectweb.asm.tree.InsnNode;
10+
import org.objectweb.asm.tree.LabelNode;
11+
import org.objectweb.asm.tree.LocalVariableNode;
12+
import org.objectweb.asm.tree.MethodInsnNode;
13+
import org.objectweb.asm.tree.MethodNode;
14+
import org.objectweb.asm.tree.VarInsnNode;
15+
16+
import java.lang.instrument.IllegalClassFormatException;
17+
import java.util.Arrays;
18+
import java.util.List;
19+
import java.util.Optional;
20+
21+
/**
22+
* @author embeddedt (credit to Uncandango for finding the bug in FML: https://github.com/neoforged/FancyModLoader/issues/289)
23+
*/
24+
public class TransformerDiscovererConstantsTransformer implements RuntimeTransformer {
25+
@Override
26+
public List<String> getTransformedClasses() {
27+
return Arrays.asList("net/neoforged/fml/loading/TransformerDiscovererConstants");
28+
}
29+
30+
@Override
31+
public int getWriteFlags() {
32+
return ClassWriter.COMPUTE_MAXS;
33+
}
34+
35+
@Override
36+
public void transformClass(ClassNode data) throws IllegalClassFormatException {
37+
for (MethodNode method : data.methods) {
38+
if (method.name.equals("shouldLoadInServiceLayer")) {
39+
Type desc = Type.getMethodType(method.desc);
40+
if (desc.getArgumentCount() == 1) {
41+
Type firstArg = desc.getArgumentTypes()[0];
42+
if (firstArg.getClassName().equals("java.nio.file.Path") || firstArg.getClassName().equals("java.util.Collection")) {
43+
// This is our method, we need to capture the JarContents.of call into a local var and restore
44+
// it after.
45+
Optional<MethodInsnNode> delegateCall = RuntimeTransformer.streamInsnList(method.instructions)
46+
.filter(n -> n instanceof MethodInsnNode)
47+
.map(MethodInsnNode.class::cast)
48+
.filter(m -> m.name.equals("shouldLoadInServiceLayer"))
49+
.findFirst();
50+
Optional<AbstractInsnNode> returnInstruction = RuntimeTransformer.streamInsnList(method.instructions)
51+
.filter(i -> i.getOpcode() == Opcodes.IRETURN).findFirst();
52+
if (delegateCall.isPresent() && returnInstruction.isPresent()) {
53+
int id = RuntimeTransformer.nextLocalVariableIndex(method);
54+
LabelNode startLabel = new LabelNode();
55+
LabelNode endLabel = new LabelNode();
56+
LocalVariableNode localVar = new LocalVariableNode(
57+
"jarContents",
58+
"Lcpw/mods/jarhandling/JarContents;",
59+
null,
60+
startLabel,
61+
endLabel,
62+
id
63+
);
64+
method.localVariables.add(localVar);
65+
InsnList prologueSave = new InsnList();
66+
prologueSave.add(startLabel);
67+
prologueSave.add(new InsnNode(Opcodes.DUP));
68+
prologueSave.add(new VarInsnNode(Opcodes.ASTORE, id));
69+
method.instructions.insertBefore(delegateCall.get(), prologueSave);
70+
InsnList epilogueClose = new InsnList();
71+
epilogueClose.add(new VarInsnNode(Opcodes.ALOAD, id));
72+
epilogueClose.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/io/Closeable", "close", "()V", true));
73+
epilogueClose.add(endLabel);
74+
method.instructions.insertBefore(returnInstruction.get(), epilogueClose);
75+
System.out.println("Patched shouldLoadInServiceLayer" + method.desc + " to close JarContents");
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)