From 30307d823149e168e55ee98dbfa884887bd6025d Mon Sep 17 00:00:00 2001 From: RaphiMC <50594595+RaphiMC@users.noreply.github.com> Date: Sun, 23 Apr 2023 02:09:59 +0200 Subject: [PATCH] Curse the Java17ToJava8 converter to allow conversion of Geyser --- .../viaproxy/injection/Java17ToJava8.java | 154 +++++++++++++++++- 1 file changed, 146 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/raphimc/viaproxy/injection/Java17ToJava8.java b/src/main/java/net/raphimc/viaproxy/injection/Java17ToJava8.java index 3cdbaf9..3e95f4d 100644 --- a/src/main/java/net/raphimc/viaproxy/injection/Java17ToJava8.java +++ b/src/main/java/net/raphimc/viaproxy/injection/Java17ToJava8.java @@ -40,7 +40,7 @@ public class Java17ToJava8 implements IBytecodeTransformer { @Override public byte[] transform(String className, byte[] bytecode) { - if (!className.startsWith("com.mojang")) return null; + if (!className.startsWith("com.mojang") && !className.startsWith("org.geysermc")) return null; ClassNode classNode = ASMUtils.fromBytes(bytecode); if (classNode.version <= Opcodes.V1_8) return null; @@ -48,7 +48,11 @@ public class Java17ToJava8 implements IBytecodeTransformer { classNode.version = Opcodes.V1_8; this.makePublic(classNode); this.convertStringConcatFactory(classNode); - this.convertCollections(classNode); + this.convertListMethods(classNode); + this.convertSetMethods(classNode); + this.convertMapMethods(classNode); + this.convertStreamMethods(classNode); + this.convertMiscMethods(classNode); this.removeRecords(classNode); return ASMUtils.toBytes(classNode, this.classProvider); @@ -94,15 +98,62 @@ public class Java17ToJava8 implements IBytecodeTransformer { } } - private void convertCollections(final ClassNode node) { + private void convertListMethods(final ClassNode node) { for (MethodNode method : node.methods) { for (AbstractInsnNode insn : method.instructions.toArray()) { if (insn.getOpcode() == Opcodes.INVOKESTATIC) { - MethodInsnNode min = (MethodInsnNode) insn; + final MethodInsnNode min = (MethodInsnNode) insn; + if (!min.owner.equals("java/util/List")) continue; - Type[] args = Type.getArgumentTypes(min.desc); - InsnList list = new InsnList(); - if (min.owner.equals("java/util/Set") && min.name.equals("of")) { + final InsnList list = new InsnList(); + + if (min.name.equals("of")) { + final Type[] args = Type.getArgumentTypes(min.desc); + if (args.length != 1 || args[0].getSort() != Type.ARRAY) { + int freeVarIndex = ASMUtils.getFreeVarIndex(method); + + int argCount = args.length; + list.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList")); + list.add(new InsnNode(Opcodes.DUP)); + list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "", "()V")); + list.add(new VarInsnNode(Opcodes.ASTORE, freeVarIndex)); + for (int i = 0; i < argCount; i++) { + list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex)); + list.add(new InsnNode(Opcodes.SWAP)); + list.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z")); + list.add(new InsnNode(Opcodes.POP)); + } + list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex)); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableList", "(Ljava/util/List;)Ljava/util/List;")); + } + } else if (min.name.equals("copyOf")) { + // TODO: Fix + /*list.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList")); + list.add(new InsnNode(Opcodes.DUP)); + list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "", "(Ljava/util/Collection;)V"));*/ + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableList", "(Ljava/util/List;)Ljava/util/List;")); + } + + if (list.size() != 0) { + method.instructions.insertBefore(insn, list); + method.instructions.remove(insn); + } + } + } + } + } + + private void convertSetMethods(final ClassNode node) { + for (MethodNode method : node.methods) { + for (AbstractInsnNode insn : method.instructions.toArray()) { + if (insn.getOpcode() == Opcodes.INVOKESTATIC) { + final MethodInsnNode min = (MethodInsnNode) insn; + if (!min.owner.equals("java/util/Set")) continue; + + final InsnList list = new InsnList(); + + if (min.name.equals("of")) { + final Type[] args = Type.getArgumentTypes(min.desc); if (args.length != 1 || args[0].getSort() != Type.ARRAY) { int freeVarIndex = ASMUtils.getFreeVarIndex(method); @@ -120,13 +171,100 @@ public class Java17ToJava8 implements IBytecodeTransformer { list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex)); list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableSet", "(Ljava/util/Set;)Ljava/util/Set;")); } - } else if (min.owner.equals("java/util/Map") && min.name.equals("of")) { + } else if (min.name.equals("copyOf")) { + // TODO: Fix + /*list.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashSet")); + list.add(new InsnNode(Opcodes.DUP)); + list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashSet", "", "(Ljava/util/Collection;)V"));*/ + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableSet", "(Ljava/util/Set;)Ljava/util/Set;")); + } + + + if (list.size() != 0) { + method.instructions.insertBefore(insn, list); + method.instructions.remove(insn); + } + } + } + } + } + + private void convertMapMethods(final ClassNode node) { + for (MethodNode method : node.methods) { + for (AbstractInsnNode insn : method.instructions.toArray()) { + if (insn.getOpcode() == Opcodes.INVOKESTATIC) { + final MethodInsnNode min = (MethodInsnNode) insn; + if (!min.owner.equals("java/util/Map")) continue; + + final InsnList list = new InsnList(); + + if (min.name.equals("of")) { + final Type[] args = Type.getArgumentTypes(min.desc); if (args.length == 0) { list.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashMap")); list.add(new InsnNode(Opcodes.DUP)); list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashMap", "", "()V")); } } + + if (list.size() != 0) { + method.instructions.insertBefore(insn, list); + method.instructions.remove(insn); + } + } + } + } + } + + private void convertStreamMethods(final ClassNode node) { + for (MethodNode method : node.methods) { + for (AbstractInsnNode insn : method.instructions.toArray()) { + if (insn.getOpcode() == Opcodes.INVOKEINTERFACE) { + final MethodInsnNode min = (MethodInsnNode) insn; + if (!min.owner.equals("java/util/stream/Stream")) continue; + + final InsnList list = new InsnList(); + + if (min.name.equals("toList")) { + int freeVarIndex = ASMUtils.getFreeVarIndex(method); + list.add(new VarInsnNode(Opcodes.ASTORE, freeVarIndex)); + + list.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList")); + list.add(new InsnNode(Opcodes.DUP)); + list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex)); + list.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/stream/Stream", "toArray", "()[Ljava/lang/Object;")); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList", "([Ljava/lang/Object;)Ljava/util/List;")); + list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "", "(Ljava/util/Collection;)V")); + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableList", "(Ljava/util/List;)Ljava/util/List;")); + } + + if (list.size() != 0) { + method.instructions.insertBefore(insn, list); + method.instructions.remove(insn); + } + } + } + } + } + + private void convertMiscMethods(final ClassNode node) { + for (MethodNode method : node.methods) { + for (AbstractInsnNode insn : method.instructions.toArray()) { + if (insn instanceof MethodInsnNode) { + final MethodInsnNode min = (MethodInsnNode) insn; + final InsnList list = new InsnList(); + + if (min.owner.equals("java/lang/String")) { + if (min.name.equals("isBlank") && min.getOpcode() == Opcodes.INVOKEVIRTUAL) { + list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/String", "trim", "()Ljava/lang/String;")); + list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z")); + } + } else if (min.owner.equals("java/io/InputStream")) { + if (min.name.equals("readAllBytes") && min.getOpcode() == Opcodes.INVOKEVIRTUAL) { + list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/apache/commons/io/IOUtils", "toByteArray", "(Ljava/io/InputStream;)[B")); + } + } + if (list.size() != 0) { method.instructions.insertBefore(insn, list); method.instructions.remove(insn);