diff options
Diffstat (limited to 'src/main/java/moe/nea/modernjava/launch/transform')
-rw-r--r-- | src/main/java/moe/nea/modernjava/launch/transform/BasePatch.java | 90 | ||||
-rw-r--r-- | src/main/java/moe/nea/modernjava/launch/transform/PatchObjectHolderRef.java | 100 |
2 files changed, 190 insertions, 0 deletions
diff --git a/src/main/java/moe/nea/modernjava/launch/transform/BasePatch.java b/src/main/java/moe/nea/modernjava/launch/transform/BasePatch.java new file mode 100644 index 0000000..1ef494c --- /dev/null +++ b/src/main/java/moe/nea/modernjava/launch/transform/BasePatch.java @@ -0,0 +1,90 @@ +package moe.nea.modernjava.launch.transform; + +import net.minecraft.launchwrapper.IClassTransformer; +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.util.Printer; +import org.objectweb.asm.util.Textifier; +import org.objectweb.asm.util.TraceMethodVisitor; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public abstract class BasePatch implements IClassTransformer { + protected abstract String getTargetedName(); + + protected Type getTargetedType() { + return getClassType(getTargetedName()); + } + + protected abstract ClassNode transform(ClassNode classNode); + + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (basicClass == null) return null; + if (!getTargetedName().equals(name)) return basicClass; + ClassNode node = new ClassNode(); + ClassReader reader = new ClassReader(basicClass); + reader.accept(node, 0); + ClassNode processedNode = transform(node); + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + processedNode.accept(writer); + return writer.toByteArray(); + } + + protected Iterable<AbstractInsnNode> iterableInstructions(InsnList insns) { + return new Iterable<AbstractInsnNode>() { + @NotNull + @Override + public Iterator<AbstractInsnNode> iterator() { + return insns.iterator(); + } + }; + } + + private Printer printer = new Textifier(); + private TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(printer); + + protected String debugInsn(AbstractInsnNode insnNode) { + insnNode.accept(traceMethodVisitor); + StringWriter sw = new StringWriter(); + printer.print(new PrintWriter(sw)); + printer.getText().clear(); + return sw.toString(); + } + + protected List<String> debugInsnList(InsnList list) { + List<String> strings = new ArrayList<>(); + for (AbstractInsnNode node : iterableInstructions(list)) { + strings.add(debugInsn(node)); + } + return strings; + } + + protected Type getClassType(String plainName) { + return Type.getObjectType(plainName.replace('.', '/')); + } + + protected static MethodNode findMethod(ClassNode node, String name, Type desc) { + System.out.println("Searching for " + name + " " + desc.getDescriptor()); + for (MethodNode method : node.methods) { + System.out.println(" - Candidate: " + method.name + " " + method.desc); + if (name.equals(method.name) && desc.getDescriptor().equals(method.desc)) { + System.out.println("Found method"); + return method; + } + } + System.out.println("Could not find method."); + return null; + } + +} diff --git a/src/main/java/moe/nea/modernjava/launch/transform/PatchObjectHolderRef.java b/src/main/java/moe/nea/modernjava/launch/transform/PatchObjectHolderRef.java new file mode 100644 index 0000000..baef356 --- /dev/null +++ b/src/main/java/moe/nea/modernjava/launch/transform/PatchObjectHolderRef.java @@ -0,0 +1,100 @@ +package moe.nea.modernjava.launch.transform; + + +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +import java.lang.reflect.Field; + +public class PatchObjectHolderRef extends BasePatch { + @Override + protected String getTargetedName() { + return "net.minecraftforge.fml.common.registry.ObjectHolderRef"; + } + + @Override + protected ClassNode transform(ClassNode classNode) { + patchFindWriteable(classNode); + patchApply(classNode); + return classNode; + } + + private void patchApply(ClassNode classNode) { + MethodNode apply = findMethod( + classNode, "apply", + Type.getMethodType(Type.VOID_TYPE) + ); + assert apply != null; + + InsnList insns = apply.instructions; + + AbstractInsnNode start = null, end = null; + int c = 0; + for (AbstractInsnNode instruction : iterableInstructions(insns)) { + if (instruction instanceof FieldInsnNode && instruction.getOpcode() == Opcodes.GETSTATIC && ((FieldInsnNode) instruction).name.equals("newFieldAccessor")) { + start = instruction; + } + if (instruction.getOpcode() == Opcodes.INVOKEVIRTUAL && start != null) { + c++; + if (c == 2) { + end = instruction.getNext(); + break; + } + } + } + AbstractInsnNode trueStart = start; + { + InsnList insertion = new InsnList(); + insertion.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insertion.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraftforge/fml/common/registry/ObjectHolderRef", "field", "Ljava/lang/reflect/Field;")); + insertion.add(new VarInsnNode(Opcodes.ALOAD, 1)); + insertion.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + "moe/nea/modernjava/launch/util/ObjectHolderRefCompanion", + "doFieldWrite", + "(Ljava/lang/reflect/Field;Ljava/lang/Object;)V", + false)); + insertion.add(new InsnNode(Opcodes.RETURN)); + insns.insertBefore(trueStart, insertion); + } + apply.maxLocals = 0; + AbstractInsnNode toRemove = start; + while (true) { + AbstractInsnNode next = toRemove.getNext(); + insns.remove(toRemove); + if (end == toRemove) + break; + toRemove = next; + } + } + + Type companionType = getClassType("moe.nea.modernjava.launch.util.ObjectHolderRefCompanion"); + + private void patchFindWriteable(ClassNode classNode) { + MethodNode makeWriteable = findMethod( + classNode, "makeWritable", + Type.getMethodType(Type.VOID_TYPE, Type.getType(Field.class)) + ); + assert makeWriteable != null; + makeWriteable.tryCatchBlocks.clear(); + InsnList insns = makeWriteable.instructions; + insns.clear(); + insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); + insns.add(new MethodInsnNode( + Opcodes.INVOKESTATIC, + companionType.getInternalName(), + "makeFieldWritable", + Type.getMethodType(Type.VOID_TYPE, Type.getType(Field.class)).getDescriptor(), + false + )); + insns.add(new InsnNode(Opcodes.RETURN)); + } +} |