more ASM tweaks

This commit is contained in:
FalsePattern committed 2022-07-23 18:55:58 +02:00
1 parent 7ff1c55036
commit 763aa9889a
2 files changed
+74 -65

No files matched your search

@@ -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();
}
@@ -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<String> 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<T extends AbstractInsnNode> extends BiFunction<T, Memory, State<AbstractInsnNode>> {}
private static State<AbstractInsnNode> 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!");
}
}