package codechicken.lib.asm; import codechicken.lib.config.ConfigFile; import codechicken.lib.config.DefaultingConfigFile; 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.tree.*; import org.objectweb.asm.util.ASMifier; import org.objectweb.asm.util.Textifier; import org.objectweb.asm.util.TraceClassVisitor; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; public class ASMHelper { public static ConfigFile config = loadConfig(); public static Logger logger = LogManager.getLogger("CCL ASM"); private static ConfigFile loadConfig() { try {//weak reference for environments without FML File mcDir = (File) ((Object[]) Class.forName("net.minecraftforge.fml.relauncher.FMLInjectionData").getMethod("data").invoke(null))[6]; File file = new File(mcDir, "config/CodeChickenLib.cfg"); if (ObfMapping.obfuscated) { return new DefaultingConfigFile(file); } else { return new ConfigFile(file).setComment("CodeChickenLib development configuration file."); } } catch (Exception ignored) { return null;//no config for these systems } } public static interface Acceptor { public void accept(ClassVisitor cv) throws IOException; } public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode) { for (MethodNode mnode : cnode.methods) { if (methodmap.matches(mnode)) { return mnode; } } return null; } public static FieldNode findField(ObfMapping fieldmap, ClassNode cnode) { for (FieldNode fnode : cnode.fields) { if (fieldmap.matches(fnode)) { return fnode; } } return null; } public static ClassNode createClassNode(byte[] bytes) { return createClassNode(bytes, 0); } public static ClassNode createClassNode(byte[] bytes, int flags) { ClassNode cnode = new ClassNode(); ClassReader reader = new ClassReader(bytes); reader.accept(cnode, flags); return cnode; } public static byte[] createBytes(ClassNode cnode, int flags) { ClassWriter cw = new CC_ClassWriter(flags); cnode.accept(cw); return cw.toByteArray(); } public static Map<LabelNode, LabelNode> cloneLabels(InsnList list) { return new InsnListSection(list).cloneLabels(); } public static InsnList cloneInsnList(InsnList list) { return new InsnListSection(list).copy().list; } public static InsnList cloneInsnList(Map<LabelNode, LabelNode> labelMap, InsnList list) { return new InsnListSection(list).copy(labelMap).list; } public static List<TryCatchBlockNode> cloneTryCatchBlocks(Map<LabelNode, LabelNode> labelMap, List<TryCatchBlockNode> tcblocks) { ArrayList<TryCatchBlockNode> clone = new ArrayList<TryCatchBlockNode>(); for (TryCatchBlockNode node : tcblocks) { clone.add(new TryCatchBlockNode(labelMap.get(node.start), labelMap.get(node.end), labelMap.get(node.handler), node.type)); } return clone; } public static List<LocalVariableNode> cloneLocals(Map<LabelNode, LabelNode> labelMap, List<LocalVariableNode> locals) { ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>(locals.size()); for (LocalVariableNode node : locals) { clone.add(new LocalVariableNode(node.name, node.desc, node.signature, labelMap.get(node.start), labelMap.get(node.end), node.index)); } return clone; } public static void copy(MethodNode src, MethodNode dst) { Map<LabelNode, LabelNode> labelMap = cloneLabels(src.instructions); dst.instructions = cloneInsnList(labelMap, src.instructions); dst.tryCatchBlocks = cloneTryCatchBlocks(labelMap, src.tryCatchBlocks); if (src.localVariables != null) { dst.localVariables = cloneLocals(labelMap, src.localVariables); } dst.visibleAnnotations = src.visibleAnnotations; dst.invisibleAnnotations = src.invisibleAnnotations; dst.visitMaxs(src.maxStack, src.maxLocals); } public static String toString(InsnList list) { return new InsnListSection(list).toString(); } public static int getLocal(List<LocalVariableNode> list, String name) { int found = -1; for (LocalVariableNode node : list) { if (node.name.equals(name)) { if (found >= 0) { throw new RuntimeException("Duplicate local variable: " + name + " not coded to handle this scenario."); } found = node.index; } } return found; } public static void replaceMethod(MethodNode original, MethodNode replacement) { original.instructions.clear(); if (original.localVariables != null) { original.localVariables.clear(); } if (original.tryCatchBlocks != null) { original.tryCatchBlocks.clear(); } replacement.accept(original); } public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals, boolean textify) { try { if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } if (!file.exists()) { file.createNewFile(); } PrintWriter pout = new PrintWriter(file); ClassVisitor cv = new TraceClassVisitor(null, textify ? new Textifier() : new ASMifier(), pout); if (filterImportant) { cv = new ImportantInsnVisitor(cv); } if (sortLocals) { cv = new LocalVariablesSorterVisitor(cv); } acceptor.accept(cv); pout.close(); } catch (IOException e) { throw new RuntimeException(e); } } public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals) { dump(acceptor, file, filterImportant, sortLocals, config.getTag("textify").getBooleanValue(true)); } public static void dump(final byte[] bytes, File file, boolean filterImportant, boolean sortLocals) { dump(new Acceptor() { @Override public void accept(ClassVisitor cv) { new ClassReader(bytes).accept(cv, ClassReader.EXPAND_FRAMES); } }, file, filterImportant, sortLocals); } public static void dump(final InputStream is, File file, boolean filterImportant, boolean sortLocals) { dump(new Acceptor() { @Override public void accept(ClassVisitor cv) throws IOException { new ClassReader(is).accept(cv, ClassReader.EXPAND_FRAMES); } }, file, filterImportant, sortLocals); } public static void dump(final ClassNode cnode, File file, boolean filterImportant, boolean sortLocals) { dump(new Acceptor() { @Override public void accept(ClassVisitor cv) { cnode.accept(cv); } }, file, filterImportant, sortLocals); } }