package net.minecraftforge.fml.common.asm.transformers; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.TreeSet; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraftforge.fml.common.FMLLog; import org.apache.commons.lang3.JavaVersion; import org.apache.commons.lang3.SystemUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import com.google.common.collect.ImmutableSet; public class BlamingTransformer implements IClassTransformer { private static final Map<String, String> classMap = new HashMap<String, String>(); private static final Set<String> naughtyMods = new HashSet<String>(); private static final Set<String> naughtyClasses = new TreeSet<String>(); private static final Set<String> orphanNaughtyClasses = new HashSet(); @Override public byte[] transform(String name, String transformedName, byte[] bytes) { if (bytes == null) { return null; } ClassReader classReader = new ClassReader(bytes); VersionVisitor visitor = new VersionVisitor(); classReader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG); return bytes; } public static void blame(String modId, String cls) { naughtyClasses.add(cls); naughtyMods.add(modId); FMLLog.severe("Unsupported class format in mod %s: class %s", modId, cls); } public static class VersionVisitor extends ClassVisitor { public VersionVisitor() { super(Opcodes.ASM5); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if( (version == Opcodes.V1_8 && !SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_8)) || (version == Opcodes.V1_7 && !SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_7)) ) { if(classMap.containsKey(name)) blame(classMap.get(name), name); else orphanNaughtyClasses.add(name); } } } private static void checkPendingNaughty() { ImmutableSet.Builder<String> toRemove = ImmutableSet.builder(); for(String cls : orphanNaughtyClasses) { if(classMap.containsKey(cls)) { String modId = classMap.get(cls); blame(modId, cls); toRemove.add(cls); } } orphanNaughtyClasses.removeAll(toRemove.build()); } public static void addClasses(String modId, Set<String> classList) { for(String cls : classList) { classMap.put(cls, modId); } checkPendingNaughty(); } public static void onCrash(StringBuilder builder) { checkPendingNaughty(); if(!naughtyClasses.isEmpty()) { builder.append("\n*** ATTENTION: detected classes with unsupported format ***\n"); builder.append("*** DO NOT SUBMIT THIS CRASH REPORT TO FORGE ***\n\n"); if(!naughtyMods.isEmpty()) { builder.append("Contact authors of the following mods: \n"); for(String modId : naughtyMods) { builder.append(" ").append(modId).append("\n"); } } if(!orphanNaughtyClasses.isEmpty()) { builder.append("Unidentified unsupported classes: \n"); for(String cls : orphanNaughtyClasses) { builder.append(" ").append(cls).append("\n"); } } builder.append('\n'); } } }