package cpw.mods.fml.common.asm.transformers;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.Level;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.asm.transformers.deobf.FMLRemappingAdapter;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.LaunchClassLoader;
/**
* FML does this as part of the deobfuscation transformer (see {@link FMLRemappingAdapter}).
* Some mods rely on it (mostly un-updated 1.7.2 mods running in 1.7.10?)
*
* Ideally this should be removed.
*/
public class StaticFieldFixingTransformer implements IClassTransformer {
private static Map<String, String> fieldTypes = new HashMap<String, String>();
private static Set<String> seenClasses = new HashSet<String>();
@Override
public byte[] transform(String arg0, String arg1, byte[] arg2) {
if(arg2 == null) return null;
if(arg0.startsWith("net.minecraft.")) {
new ClassReader(arg2).accept(new ClassVisitor(Opcodes.ASM5) {
String classInternalName;
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classInternalName = name;
seenClasses.add(name);
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if((access & Opcodes.ACC_STATIC) != 0) {
fieldTypes.put(classInternalName+"/"+name, desc);
}
return null;
}
}, ClassReader.SKIP_CODE);
return arg2;
} else {
ClassWriter cw = new ClassWriter(0);
new ClassReader(arg2).accept(new ClassVisitor(Opcodes.ASM5, cw) {
String classInternalName;
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classInternalName = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions)) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if((opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC) && owner.startsWith("net/minecraft/")) {
if(!seenClasses.contains(owner)) {
try {
StaticFieldFixingTransformer.class.getClassLoader().loadClass(owner.replace('/', '.'));
} catch(ClassNotFoundException ex) {
FMLLog.log(Level.ERROR, ex, "Failed to load "+owner+" referenced by "+classInternalName);
}
}
String knownDesc = fieldTypes.get(owner+"/"+name);
if(knownDesc != null)
desc = knownDesc;
}
super.visitFieldInsn(opcode, owner, name, desc);
}
};
}
}, 0);
return cw.toByteArray();
}
}
}