From 763aa9889af6d1fb5a2d77b296e630649496e8f7 Mon Sep 17 00:00:00 2001 From: FalsePattern <30945458+FalsePattern@users.noreply.github.com> Date: Sat, 23 Jul 2022 18:55:58 +0200 Subject: [PATCH] more ASM tweaks --- .../endlessids/asm/IETransformer.java | 72 ++++++++++--------- .../ChunkProviderSuperPatcher.java | 67 ++++++++--------- 2 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java b/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java index 4d0a80c..7ff9da4 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java +++ b/src/main/java/com/falsepattern/endlessids/asm/IETransformer.java @@ -3,19 +3,16 @@ package com.falsepattern.endlessids.asm; import com.falsepattern.endlessids.Tags; import com.falsepattern.endlessids.asm.transformer.ChunkProviderSuperPatcher; import com.falsepattern.endlessids.asm.transformer.DevFixer; +import lombok.val; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.util.CheckClassAdapter; import net.minecraft.launchwrapper.IClassTransformer; -import java.util.Arrays; - public class IETransformer implements IClassTransformer { public static final Logger logger; public static boolean isObfuscated; @@ -32,44 +29,53 @@ public class IETransformer implements IClassTransformer { if (bytes == null) { return null; } - ClassEdit edit = ClassEdit.get(transformedName); - final ClassNode cn = new ClassNode(Opcodes.ASM5); - final ClassReader reader = new ClassReader(bytes); - reader.accept(cn, edit == null ? ClassReader.EXPAND_FRAMES : 0); - if (ChunkProviderSuperPatcher.isChunkProvider(cn)) { - if (edit == null) { - edit = ClassEdit.ChunkProviderSuperPatcher; + ClassEdit[] edits = new ClassEdit[2]; + edits[0] = ClassEdit.get(transformedName); + ClassNode cn = new ClassNode(Opcodes.ASM5); + { + val reader = new ClassReader(bytes); + reader.accept(cn, 0); + } + boolean expanded = false; + if (ChunkProviderSuperPatcher.shouldPatch(cn)) { + expanded = true; + { + cn = new ClassNode(Opcodes.ASM5); + val reader = new ClassReader(bytes); + reader.accept(cn, ClassReader.EXPAND_FRAMES); + } + if (edits[0] == null) { + edits[0] = ClassEdit.ChunkProviderSuperPatcher; } else { - ClassEdit.ChunkProviderSuperPatcher.getTransformer().transform(cn, isObfuscated); + edits[1] = ClassEdit.ChunkProviderSuperPatcher; } } - if (edit == null) { + if (edits[0] == null) { return isObfuscated ? bytes : DevFixer.fixDev(bytes); } if (!isObfuscated) { DevFixer.transform(cn); } - IETransformer.logger.debug("Patching {} with {}...", transformedName, edit.getName()); - try { - edit.getTransformer().transform(cn, IETransformer.isObfuscated); - } catch (AsmTransformException t) { - IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(), - t.getMessage()); - throw t; - } catch (Throwable t2) { - IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(), - t2.getMessage()); - throw new RuntimeException(t2); - } - final ClassWriter writer = new ClassWriter(edit == ClassEdit.ChunkProviderSuperPatcher ? ClassWriter.COMPUTE_FRAMES : 0); - try { - final ClassVisitor check = new CheckClassAdapter(writer); - cn.accept(check); - } catch (Throwable t3) { - IETransformer.logger.error("Error verifying {} transformed with {}: {}", transformedName, edit.getName(), - t3.getMessage()); - throw new RuntimeException(t3); + for (int i = 0; i < edits.length; i++) { + val edit = edits[i]; + if (edit == null) { + continue; + } + IETransformer.logger.debug("Patching {} with {}...", transformedName, edit.getName()); + try { + edit.getTransformer().transform(cn, IETransformer.isObfuscated); + } catch (AsmTransformException t) { + IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(), + t.getMessage()); + throw t; + } catch (Throwable t2) { + IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(), + t2.getMessage()); + throw new RuntimeException(t2); + } } + final ClassWriter writer = new ClassWriter(expanded ? ClassWriter.COMPUTE_FRAMES : 0); + cn.accept(writer); IETransformer.logger.debug("Patched {} successfully.", transformedName); return writer.toByteArray(); } diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java index 54a5f5a..c60e792 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java @@ -141,15 +141,32 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { } } - public static boolean isChunkProvider(final ClassNode cn) { - return //100% certain that this is a chunk provider - anyMatch(cn.interfaces, CLASS_IChunkProvider) || - //100% certain that this is a chunk provider - anyMatch(cn.superName, CLASS_ChunkProviderGenerate) || - //It *might* be a chunk provider given the name, so try patching it. - cn.name.contains("ChunkProvider") || - //Look for the specific buggy invocation as a last effort - containsBrokenCall(cn); + public static boolean shouldPatch(final ClassNode cn) { + for (MethodNode method : cn.methods) { + if (scanForBrokenCall(method)) { + return true; + } + } + return false; + } + + private static boolean scanForBrokenCall(MethodNode method) { + val instructions = method.instructions; + val insnCount = instructions.size(); + for (int i = 0; i < insnCount; i++) { + val insn = instructions.get(i); + if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) { + continue; + } + val invoke = (MethodInsnNode) insn; + + if (anyMatch(invoke.owner, CLASS_Chunk) && + anyMatch(invoke.name, METHOD_getBiomeArray) && + invoke.desc.equals("()[B")) { + return true; + } + } + return false; } private static boolean anyMatch(List str, String[] candidates) { @@ -181,27 +198,6 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { return false; } - private static boolean containsBrokenCall(final ClassNode cn) { - for (val method: cn.methods) { - val instructions = method.instructions; - val insnCount = instructions.size(); - for (int i = 0; i < insnCount; i++) { - val insn = instructions.get(i); - if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) { - continue; - } - val invoke = (MethodInsnNode) insn; - - if (anyMatch(invoke.owner, CLASS_Chunk) && - anyMatch(invoke.name, METHOD_getBiomeArray) && - invoke.desc.equals("()[B")) { - return true; - } - } - } - return false; - } - public interface State extends BiFunction> {} private static State STATE0 = null; @@ -276,7 +272,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { STATE6 = typeCheck(LabelNode.class, () -> STATE7); STATE7 = casting(FrameNode.class, (insn, memory) -> - memory.locals.get(memory.chunkIndex).equals(CLASS_Chunk) && + anyMatch((String)memory.locals.get(memory.chunkIndex), CLASS_Chunk) && memory.locals.get(memory.biomeArrayIndex).equals("[B") && memory.locals.get(memory.iteratorIndex).equals(1) ? STATE8 : STATE0); @@ -320,7 +316,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { STATE14_ABD1 = casting(VarInsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.ALOAD) { - if (memory.locals.get(insn.var).equals("[L" + CLASS_BiomeGenBase + ";")) { + if (anyMatch((String)memory.locals.get(insn.var), CLASS_BiomeGenBase, "[L", ";")) { return STATE14_ABD1_BA2; } else { return STATE14_ABD1_BD1; @@ -403,9 +399,12 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { @Override public void transform(final ClassNode cn, final boolean obfuscated) { + boolean patched = false; for (val method: cn.methods) { val memory = findTheTarget(method); if (memory != null) { + patched = true; + IETransformer.logger.debug("[ChunkProvider patcher]: Match found in method " + method.name); val insnList = method.instructions; for (val node: method.localVariables) { if (node.desc.equals("[B") && node.index == memory.biomeArrayIndex) { @@ -418,6 +417,10 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { insnList.set(insnList.get(memory.arrayStoreInsn), new InsnNode(Opcodes.SASTORE)); } } + if (!patched) { + IETransformer.logger.debug("[ChunkProvider patcher]: No matches found in class! This is not explicitly " + + "an error, because the patch might be applied by a mixin instead!"); + } }