Upgraded ASM lib

This commit is contained in:
Chicken-Bomes committed 2014-04-28 19:40:13 +10:00
1 parent 0a14b64e12
commit 52f82017e7
14 files changed
+1142 -747

No files matched your search

+173
View File
@@ -0,0 +1,173 @@
package codechicken.lib.asm;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import java.util.*;
import java.util.Map.Entry;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
public class ASMBlock
{
public InsnListSection list;
private BiMap<String, LabelNode> labels;
public ASMBlock(InsnListSection list, BiMap<String, LabelNode> labels) {
this.list = list;
this.labels = labels;
}
public ASMBlock(InsnListSection list) {
this(list, HashBiMap.<String, LabelNode>create());
}
public ASMBlock(InsnList list) {
this(new InsnListSection(list));
}
public ASMBlock() {
this(new InsnListSection());
}
public LabelNode getOrAdd(String s) {
LabelNode l = get(s);
if (l == null)
labels.put(s, l = new LabelNode());
return l;
}
public LabelNode get(String s) {
return labels.get(s);
}
public void replaceLabels(Map<LabelNode, LabelNode> labelMap, Set<LabelNode> usedLabels) {
for (AbstractInsnNode insn : list)
switch (insn.getType()) {
case LABEL:
AbstractInsnNode insn2 = insn.clone(labelMap);
if (insn2 == insn)//identity mapping
continue;
if (usedLabels.contains(insn2))
throw new IllegalStateException("LabelNode cannot be a part of two InsnLists");
list.replace(insn, insn2);
break;
case JUMP_INSN:
case FRAME:
case LOOKUPSWITCH_INSN:
case TABLESWITCH_INSN:
list.replace(insn, insn.clone(labelMap));
}
for(Entry<LabelNode, LabelNode> entry : labelMap.entrySet()) {
String key = labels.inverse().get(entry.getKey());
if(key != null)
labels.put(key, entry.getValue());
}
}
public void replaceLabels(Map<LabelNode, LabelNode> labelMap) {
replaceLabels(labelMap, Collections.EMPTY_SET);
}
public void replaceLabel(String s, LabelNode l) {
LabelNode old = get(s);
if (old != null)
replaceLabels(ImmutableMap.of(old, l));
}
/**
* Pulls all common labels from other into this
* @return this
*/
public ASMBlock mergeLabels(ASMBlock other) {
if(labels.isEmpty() || other.labels.isEmpty())
return this;
//common labels, give them our nodes
HashMap<LabelNode, LabelNode> labelMap = list.identityLabelMap();
for(Entry<String, LabelNode> entry : other.labels.entrySet()) {
LabelNode old = labels.get(entry.getKey());
if(old != null)
labelMap.put(old, entry.getValue());
}
HashSet<LabelNode> usedLabels = new HashSet<LabelNode>();
for (AbstractInsnNode insn = other.list.list.getFirst(); insn != null; insn = insn.getNext())
if(insn.getType() == LABEL)
usedLabels.add((LabelNode) insn);
replaceLabels(labelMap, usedLabels);
return this;
}
/**
* Like mergeLabels but pulls insns from other list into this so LabelNodes can be transferred
* @return this
*/
public ASMBlock pullLabels(ASMBlock other) {
other.list.remove();
return mergeLabels(other);
}
public ASMBlock copy() {
BiMap<String, LabelNode> labels = HashBiMap.create();
Map<LabelNode, LabelNode> labelMap = list.cloneLabels();
for(Entry<String, LabelNode> entry : this.labels.entrySet())
labels.put(entry.getKey(), labelMap.get(entry.getValue()));
return new ASMBlock(list.copy(labelMap), labels);
}
public ASMBlock applyLabels(InsnListSection list2) {
if(labels.isEmpty())
return new ASMBlock(list2);
Set<LabelNode> cFlowLabels1 = labels.values();
Set<LabelNode> cFlowLabels2 = InsnComparator.getControlFlowLabels(list2);
ASMBlock block = new ASMBlock(list2);
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
for(int i = 0, k = 0; i < list.size() && k < list2.size(); ) {
AbstractInsnNode insn1 = list.get(i);
if(!InsnComparator.insnImportant(insn1, cFlowLabels1)) {
i++;
continue;
}
AbstractInsnNode insn2 = list2.get(k);
if(!InsnComparator.insnImportant(insn2, cFlowLabels2)) {
k++;
continue;
}
if(insn1.getOpcode() != insn2.getOpcode())
throw new IllegalArgumentException("Lists do not match:\n"+list+"\n\n"+list2);
switch(insn1.getType()) {
case LABEL:
labelMap.put((LabelNode) insn1, (LabelNode) insn2);
break;
case JUMP_INSN:
labelMap.put(((JumpInsnNode) insn1).label, ((JumpInsnNode) insn2).label);
break;
}
i++; k++;
}
for(Entry<String, LabelNode> entry : labels.entrySet())
block.labels.put(entry.getKey(), labelMap.get(entry.getValue()));
return block;
}
public InsnList rawListCopy() {
return list.copy().list;
}
}
+77 -171
View File
@@ -1,76 +1,40 @@
package codechicken.lib.asm;
import codechicken.lib.asm.InstructionComparator.InsnListSection;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.DefaultingConfigFile;
import cpw.mods.fml.relauncher.FMLInjectionData;
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.Label;
import org.objectweb.asm.tree.*;
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.HashMap;
import java.util.List;
import java.util.Map;
public class ASMHelper
{
public static class CodeBlock
{
public Label start = new Label();
public Label end = new Label();
public static ConfigFile config = loadConfig();
public static Logger logger = LogManager.getLogger("CCL ASM");
private static ConfigFile loadConfig() {
File file = new File((File) FMLInjectionData.data()[6], "config/CodeChickenLib.cfg");
if(ObfMapping.obfuscated)
return new DefaultingConfigFile(file);
else
return new ConfigFile(file).setComment("CodeChickenLib development configuration file.");
}
public static class ForBlock extends CodeBlock
public static interface Acceptor
{
public Label cmp = new Label();
public Label inc = new Label();
public Label body = new Label();
}
public static abstract class MethodAltercator
{
public final ObfMapping method;
public MethodAltercator(ObfMapping method) {
this.method = method.toClassloading();
}
public abstract void alter(MethodNode mv);
}
public static abstract class MethodWriter
{
public final int access;
public final ObfMapping method;
public final String[] exceptions;
public MethodWriter(int access, ObfMapping method) {
this(access, method, null);
}
public MethodWriter(int access, ObfMapping method, String[] exceptions) {
this.access = access;
this.method = method;
this.exceptions = exceptions;
}
public abstract void write(MethodNode mv);
}
public static class MethodInjector
{
public final ObfMapping method;
public final InsnList needle;
public final InsnList injection;
public final boolean before;
public MethodInjector(ObfMapping method, InsnList needle, InsnList injection, boolean before) {
this.method = method;
this.needle = needle;
this.injection = injection;
this.before = before;
}
public void accept(ClassVisitor cv) throws IOException;
}
public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode) {
@@ -104,91 +68,16 @@ public class ASMHelper
return cw.toByteArray();
}
public static byte[] writeMethods(String name, byte[] bytes, Multimap<String, MethodWriter> writers) {
if (writers.containsKey(name)) {
ClassNode cnode = createClassNode(bytes);
for (MethodWriter mw : writers.get(name)) {
MethodNode mv = findMethod(mw.method, cnode);
if (mv == null)
mv = (MethodNode) cnode.visitMethod(mw.access, mw.method.s_name, mw.method.s_desc, null, mw.exceptions);
mv.access = mw.access;
mv.instructions.clear();
mw.write(mv);
}
bytes = createBytes(cnode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
}
return bytes;
public static Map<LabelNode, LabelNode> cloneLabels(InsnList list) {
return new InsnListSection(list).cloneLabels();
}
public static byte[] injectMethods(String name, byte[] bytes, Multimap<String, MethodInjector> injectors) {
if (injectors.containsKey(name)) {
ClassNode cnode = createClassNode(bytes);
for (MethodInjector injector : injectors.get(name)) {
MethodNode method = findMethod(injector.method, cnode);
if (method == null)
throw new RuntimeException("Method not found: " + injector.method);
System.out.println("Injecting into " + injector.method + "\n" + printInsnList(injector.injection));
List<AbstractInsnNode> callNodes;
if (injector.before)
callNodes = InstructionComparator.insnListFindStart(method.instructions, injector.needle);
else
callNodes = InstructionComparator.insnListFindEnd(method.instructions, injector.needle);
if (callNodes.size() == 0) {
throw new RuntimeException("Needle not found in Haystack: " + injector.method + "\n" + printInsnList(injector.needle));
}
for (AbstractInsnNode node : callNodes) {
if (injector.before) {
System.out.println("Injected before: " + printInsn(node));
method.instructions.insertBefore(node, cloneInsnList(injector.injection));
} else {
System.out.println("Injected after: " + printInsn(node));
method.instructions.insert(node, cloneInsnList(injector.injection));
}
}
}
bytes = createBytes(cnode, ClassWriter.COMPUTE_FRAMES);
}
return bytes;
public static InsnList cloneInsnList(InsnList list) {
return new InsnListSection(list).copy().list;
}
public static String printInsnList(InsnList list) {
InsnListPrinter p = new InsnListPrinter();
p.visitInsnList(list);
return p.textString();
}
public static String printInsn(AbstractInsnNode insn) {
InsnListPrinter p = new InsnListPrinter();
p.visitInsn(insn);
return p.textString();
}
public static Map<LabelNode, LabelNode> cloneLabels(InsnList insns) {
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
for (AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
if (insn.getType() == 8)
labelMap.put((LabelNode) insn, new LabelNode());
return labelMap;
}
public static InsnList cloneInsnList(InsnList insns) {
return cloneInsnList(cloneLabels(insns), insns);
}
public static InsnList cloneInsnList(Map<LabelNode, LabelNode> labelMap, InsnList insns) {
InsnList clone = new InsnList();
for (AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
clone.add(insn.clone(labelMap));
return clone;
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) {
@@ -204,7 +93,7 @@ public class ASMHelper
}
public static List<LocalVariableNode> cloneLocals(Map<LabelNode, LabelNode> labelMap, List<LocalVariableNode> locals) {
ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>();
ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>(locals.size());
for (LocalVariableNode node : locals)
clone.add(new LocalVariableNode(
node.name, node.desc, node.signature,
@@ -226,28 +115,8 @@ public class ASMHelper
dst.visitMaxs(src.maxStack, src.maxLocals);
}
public static byte[] alterMethods(String name, byte[] bytes, HashMultimap<String, MethodAltercator> altercators) {
if (altercators.containsKey(name)) {
ClassNode cnode = createClassNode(bytes);
for (MethodAltercator injector : altercators.get(name)) {
MethodNode method = findMethod(injector.method, cnode);
if (method == null)
throw new RuntimeException("Method not found: " + injector.method);
injector.alter(method);
}
bytes = createBytes(cnode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
}
return bytes;
}
public static String printInsnList(InsnListSection subsection) {
InsnListPrinter p = new InsnListPrinter();
p.visitInsnList(subsection);
return p.textString();
public static String toString(InsnList list) {
return new InsnListSection(list).toString();
}
public static int getLocal(List<LocalVariableNode> list, String name) {
@@ -263,7 +132,7 @@ public class ASMHelper
return found;
}
public static void replaceMethodCode(MethodNode original, MethodNode replacement) {
public static void replaceMethod(MethodNode original, MethodNode replacement) {
original.instructions.clear();
if (original.localVariables != null)
original.localVariables.clear();
@@ -272,14 +141,51 @@ public class ASMHelper
replacement.accept(original);
}
public static void removeBlock(InsnList insns, InsnListSection block) {
AbstractInsnNode insn = block.first;
while (true) {
AbstractInsnNode next = insn.getNext();
insns.remove(insn);
if (insn == block.last)
break;
insn = next;
public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals) {
try {
if(!file.getParentFile().exists())
file.getParentFile().mkdirs();
if(!file.exists())
file.createNewFile();
PrintWriter pout = new PrintWriter(file);
ClassVisitor cv = new TraceClassVisitor(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(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);
}
}
+52 -107
View File
@@ -1,67 +1,22 @@
package codechicken.lib.asm;
import com.google.common.collect.ImmutableMap;
import org.objectweb.asm.tree.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
public class ASMReader
{
public static class ASMBlock
{
public InsnList insns = new InsnList();
private HashMap<String, LabelNode> labels = new HashMap<String, LabelNode>();
public LabelNode getOrAdd(String s)
{
LabelNode l = get(s);
if(l == null)
labels.put(s, l = new LabelNode());
return l;
}
public LabelNode get(String s)
{
return labels.get(s);
}
public void replace(String s, LabelNode l)
{
LabelNode old = get(s);
if(old != null)
{
Map<LabelNode, LabelNode> map = ImmutableMap.of(old, l);
for(AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
{
switch(insn.getType())
{
case JUMP_INSN:
case FRAME:
case LOOKUPSWITCH_INSN:
case TABLESWITCH_INSN:
case LABEL:
insns.insert(insn, insn.clone(map));
insns.remove(insn);
}
}
}
labels.put(s, l);
}
}
public static Map<String, Integer> opCodes = new HashMap<String, Integer>();
public static byte[] TYPE;
static
{
static {
opCodes.put("NOP", NOP);
opCodes.put("ACONST_NULL", ACONST_NULL);
opCodes.put("ICONST_M1", ICONST_M1);
@@ -219,7 +174,7 @@ public class ASMReader
opCodes.put("MULTIANEWARRAY", MULTIANEWARRAY);
opCodes.put("IFNULL", IFNULL);
opCodes.put("IFNONNULL", IFNONNULL);
//derived from classWriter, mapped to AbstractInsnNode
TYPE = new byte[200];
String s = "AAAAAAAAAAAAAAAABBJ__CCCCC____________________AAAAAAAACC"
@@ -230,95 +185,89 @@ public class ASMReader
TYPE[i] = (byte) (s.charAt(i) - 'A');
}
public static Map<String, ASMBlock> loadResource(String res)
{
public static Map<String, ASMBlock> loadResource(String res) {
return loadResource(ASMHelper.class.getResourceAsStream(res), res);
}
public static Map<String, ASMBlock> loadResource(InputStream in, String res)
{
public static Map<String, ASMBlock> loadResource(InputStream in, String res) {
HashMap<String, ASMBlock> blocks = new HashMap<String, ASMBlock>();
String current = "unnamed";
ASMBlock block = new ASMBlock();
try
{
try {
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line;
while((line = r.readLine()) != null)
{
while ((line = r.readLine()) != null) {
{
int hpos = line.indexOf('#');
if(hpos >= 0) line = line.substring(0, hpos);
if (hpos >= 0) line = line.substring(0, hpos);
}
line = line.trim();
if(line.length() == 0) continue;
if(line.startsWith("list "))
{
if(block.insns.size() > 0) blocks.put(current, block);
if (line.length() == 0) continue;
if (line.startsWith("list ")) {
if (block.list.size() > 0) blocks.put(current, block);
current = line.substring(5);
block = new ASMBlock();
continue;
}
try
{
try {
AbstractInsnNode insn = null;
String[] split = line.replace(" : ", ":").split(" ");
Integer i_opcode = opCodes.get(split[0]);
if(i_opcode == null)
{
if(split[0].equals("LINENUMBER"))
if (i_opcode == null) {
if (split[0].equals("LINENUMBER"))
insn = new LineNumberNode(Integer.parseInt(split[1]), block.getOrAdd(split[2]));
else if(split[0].startsWith("L"))
else if (split[0].startsWith("L"))
insn = block.getOrAdd(split[0]);
else
throw new Exception("Unknown opcode " + split[0]);
}
else
{
} else {
int opcode = i_opcode;
switch(TYPE[opcode])
{
switch (TYPE[opcode]) {
case INSN:
insn = new InsnNode(opcode);
break;
break;
case INT_INSN:
insn = new IntInsnNode(opcode, Integer.parseInt(split[1]));
break;
break;
case VAR_INSN:
insn = new VarInsnNode(opcode, Integer.parseInt(split[1]));
break;
break;
case TYPE_INSN:
insn = new ObfMapping(split[1]).toClassloading().toInsn(opcode);
break;
break;
case FIELD_INSN:
case METHOD_INSN:
StringBuilder sb = new StringBuilder();
for(int i = 1; i < split.length; i++)
for (int i = 1; i < split.length; i++)
sb.append(split[i]);
insn = ObfMapping.fromDesc(sb.toString()).toClassloading().toInsn(opcode);
break;
break;
case INVOKE_DYNAMIC_INSN:
throw new Exception("Found INVOKEDYNAMIC while reading");
case JUMP_INSN:
insn = new JumpInsnNode(opcode, block.getOrAdd(split[1]));
break;
break;
case LDC_INSN:
String cst = split[1];
if(cst.contains(""))
insn = new LdcInsnNode(cst.substring(1, cst.length()-1));
else if(cst.endsWith("L"))
insn = new LdcInsnNode(Long.valueOf(cst.substring(0, cst.length()-1)));
else if(cst.endsWith("F"))
insn = new LdcInsnNode(Float.valueOf(cst.substring(0, cst.length()-1)));
else if(cst.endsWith("D"))
insn = new LdcInsnNode(Double.valueOf(cst.substring(0, cst.length()-1)));
if(cst.equals("*"))
insn = new LdcInsnNode(null);
else if (cst.endsWith("\""))
insn = new LdcInsnNode(cst.substring(1, cst.length() - 1));
else if (cst.endsWith("L"))
insn = new LdcInsnNode(Long.valueOf(cst.substring(0, cst.length() - 1)));
else if (cst.endsWith("F"))
insn = new LdcInsnNode(Float.valueOf(cst.substring(0, cst.length() - 1)));
else if (cst.endsWith("D"))
insn = new LdcInsnNode(Double.valueOf(cst.substring(0, cst.length() - 1)));
else if (cst.contains("."))
insn = new LdcInsnNode(Double.valueOf(cst));
else
insn = new LdcInsnNode(Integer.valueOf(cst));
break;
break;
case IINC_INSN:
insn = new IincInsnNode(opcode, Integer.parseInt(split[1]));
break;
break;
case LABEL:
throw new Exception("Use L# for labels");
case TABLESWITCH_INSN:
@@ -326,29 +275,25 @@ public class ASMReader
throw new Exception("I don't know how to deal with this insn type");
case MULTIANEWARRAY_INSN:
insn = new MultiANewArrayInsnNode(split[1], Integer.parseInt(split[2]));
break;
break;
case FRAME:
throw new Exception("Use ClassWriter.COMPUTE_FRAMES");
}
}
if(insn != null)
block.insns.add(insn);
}
catch(Exception e)
{
System.err.println("Error while reading ASM Block "+
current+" from "+res+", line: "+line);
if (insn != null)
block.list.add(insn);
} catch (Exception e) {
System.err.println("Error while reading ASM Block " +
current + " from " + res + ", line: " + line);
e.printStackTrace();
}
}
r.close();
if(block.insns.size() > 0) blocks.put(current, block);
}
catch(IOException e)
{
throw new RuntimeException("Failed to read ASM resource: "+res, e);
if (block.list.size() > 0) blocks.put(current, block);
} catch (IOException e) {
throw new RuntimeException("Failed to read ASM resource: " + res, e);
}
return blocks;
}
@@ -0,0 +1,36 @@
package codechicken.lib.asm;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.MethodNode;
public class ImportantInsnVisitor extends ClassVisitor
{
public class ImportantInsnMethodVisitor extends MethodVisitor
{
MethodVisitor delegate;
public ImportantInsnMethodVisitor(int access, String name, String desc, String signature, String[] exceptions) {
super(Opcodes.ASM4, new MethodNode(access, name, desc, signature, exceptions));
delegate = cv.visitMethod(access, name, desc, signature, exceptions);
}
@Override
public void visitEnd() {
super.visitEnd();
MethodNode mnode = (MethodNode)mv;
mnode.instructions = InsnComparator.getImportantList(mnode.instructions);
mnode.accept(delegate);
}
}
public ImportantInsnVisitor(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new ImportantInsnMethodVisitor(access, name, desc, signature, exceptions);
}
}
+189
View File
@@ -0,0 +1,189 @@
package codechicken.lib.asm;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import org.objectweb.asm.tree.*;
import java.util.*;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
public class InsnComparator
{
public static boolean varInsnEqual(VarInsnNode insn1, VarInsnNode insn2) {
return insn1.var == -1 || insn2.var == -1 || insn1.var == insn2.var;
}
public static boolean methodInsnEqual(MethodInsnNode insn1, MethodInsnNode insn2) {
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean fieldInsnEqual(FieldInsnNode insn1, FieldInsnNode insn2) {
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean ldcInsnEqual(LdcInsnNode insn1, LdcInsnNode insn2) {
return insn1.cst == null || insn2.cst == null || insn1.cst.equals(insn2.cst);
}
public static boolean typeInsnEqual(TypeInsnNode insn1, TypeInsnNode insn2) {
return insn1.desc.equals("*") || insn2.desc.equals("*") || insn1.desc.equals(insn2.desc);
}
public static boolean iincInsnEqual(IincInsnNode node1, IincInsnNode node2) {
return node1.var == node2.var && node1.incr == node2.incr;
}
public static boolean intInsnEqual(IntInsnNode node1, IntInsnNode node2) {
return node1.operand == -1 || node2.operand == -1 || node1.operand == node2.operand;
}
public static boolean insnEqual(AbstractInsnNode node1, AbstractInsnNode node2) {
if (node1.getOpcode() != node2.getOpcode())
return false;
switch (node2.getType()) {
case VAR_INSN:
return varInsnEqual((VarInsnNode) node1, (VarInsnNode) node2);
case TYPE_INSN:
return typeInsnEqual((TypeInsnNode) node1, (TypeInsnNode) node2);
case FIELD_INSN:
return fieldInsnEqual((FieldInsnNode) node1, (FieldInsnNode) node2);
case METHOD_INSN:
return methodInsnEqual((MethodInsnNode) node1, (MethodInsnNode) node2);
case LDC_INSN:
return ldcInsnEqual((LdcInsnNode) node1, (LdcInsnNode) node2);
case IINC_INSN:
return iincInsnEqual((IincInsnNode) node1, (IincInsnNode) node2);
case INT_INSN:
return intInsnEqual((IntInsnNode) node1, (IntInsnNode) node2);
default:
return true;
}
}
public static boolean insnImportant(AbstractInsnNode insn, Set<LabelNode> controlFlowLabels) {
switch(insn.getType()) {
case LINE:
case FRAME:
return false;
case LABEL:
return controlFlowLabels.contains(insn);
default:
return true;
}
}
public static Set<LabelNode> getControlFlowLabels(InsnListSection list) {
return getControlFlowLabels(list.list);
}
public static Set<LabelNode> getControlFlowLabels(InsnList list) {
HashSet<LabelNode> controlFlowLabels = new HashSet<LabelNode>();
for (AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext()) {
switch (insn.getType()) {
case JUMP_INSN:
JumpInsnNode jinsn = (JumpInsnNode) insn;
controlFlowLabels.add(jinsn.label);
break;
case TABLESWITCH_INSN:
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode) insn;
controlFlowLabels.add(tsinsn.dflt);
for (LabelNode label : tsinsn.labels)
controlFlowLabels.add(label);
break;
case LOOKUPSWITCH_INSN:
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode) insn;
controlFlowLabels.add(lsinsn.dflt);
for (LabelNode label : lsinsn.labels)
controlFlowLabels.add(label);
break;
}
}
return controlFlowLabels;
}
public static InsnList getImportantList(InsnList list) {
return getImportantList(new InsnListSection(list)).list;
}
public static InsnListSection getImportantList(InsnListSection list) {
if (list.size() == 0)
return list;
Set<LabelNode> controlFlowLabels = getControlFlowLabels(list);
Map<LabelNode, LabelNode> labelMap = Maps.asMap(controlFlowLabels, new Function<LabelNode, LabelNode>()
{
@Override
public LabelNode apply(LabelNode input) {
return input;
}
});
InsnListSection importantNodeList = new InsnListSection();
for(AbstractInsnNode insn : list)
if (insnImportant(insn, controlFlowLabels))
importantNodeList.add(insn.clone(labelMap));
return importantNodeList;
}
public static List<InsnListSection> find(InsnListSection haystack, InsnListSection needle) {
Set<LabelNode> controlFlowLabels = getControlFlowLabels(haystack);
LinkedList<InsnListSection> list = new LinkedList<InsnListSection>();
for (int start = 0; start <= haystack.size() - needle.size(); start++) {
InsnListSection section = matches(haystack.drop(start), needle, controlFlowLabels);
if (section != null) {
list.add(section);
start = section.end-1;
}
}
return list;
}
public static List<InsnListSection> find(InsnList haystack, InsnListSection needle) {
return find(new InsnListSection(haystack), needle);
}
public static InsnListSection matches(InsnListSection haystack, InsnListSection needle, Set<LabelNode> controlFlowLabels) {
int h = 0, n = 0;
for (; h < haystack.size() && n < needle.size(); h++) {
AbstractInsnNode insn = haystack.get(h);
if(!insnImportant(insn, controlFlowLabels))
continue;
if (!insnEqual(haystack.get(h), needle.get(n)))
return null;
n++;
}
if (n != needle.size())
return null;
return haystack.take(h);
}
public static InsnListSection findOnce(InsnListSection haystack, InsnListSection needle) {
List<InsnListSection> list = find(haystack, needle);
if(list.size() != 1)
throw new RuntimeException("Needle found " + list.size() + " times in Haystack:\n" + haystack + "\n\n" + needle);
return list.get(0);
}
public static InsnListSection findOnce(InsnList haystack, InsnListSection needle) {
return findOnce(new InsnListSection(haystack), needle);
}
public static List<InsnListSection> findN(InsnListSection haystack, InsnListSection needle) {
List<InsnListSection> list = find(haystack, needle);
if(list.isEmpty())
throw new RuntimeException("Needle not found in Haystack:\n" + haystack + "\n\n" + needle);
return list;
}
public static List<InsnListSection> findN(InsnList haystack, InsnListSection needle) {
return findN(new InsnListSection(haystack), needle);
}
}
@@ -1,197 +0,0 @@
package codechicken.lib.asm;
import codechicken.lib.asm.InstructionComparator.InsnListSection;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.Textifier;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
public class InsnListPrinter extends Textifier
{
private boolean buildingLabelMap = false;
public void visitInsnList(InsnList list)
{
text.clear();
if(labelNames == null)
labelNames = new HashMap<Label, String>();
else
labelNames.clear();
buildingLabelMap = true;
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
if(insn.getType() == 8)
visitLabel(((LabelNode)insn).getLabel());
text.clear();
buildingLabelMap = false;
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
_visitInsn(insn);
}
public void visitInsnList(InsnListSection subsection)
{
text.clear();
if(labelNames == null)
labelNames = new HashMap<Label, String>();
else
labelNames.clear();
buildingLabelMap = true;
{
AbstractInsnNode insn = subsection.first;
while(true)
{
if(insn.getType() == 8)
visitLabel(((LabelNode)insn).getLabel());
if(insn == subsection.last)
break;
insn = insn.getNext();
}
}
text.clear();
buildingLabelMap = false;
{
AbstractInsnNode insn = subsection.first;
while(true)
{
_visitInsn(insn);
if(insn == subsection.last)
break;
insn = insn.getNext();
}
}
}
public void visitInsn(AbstractInsnNode insn)
{
text.clear();
if(labelNames == null)
labelNames = new HashMap<Label, String>();
else
labelNames.clear();
_visitInsn(insn);
}
private void _visitInsn(AbstractInsnNode insn)
{
switch(insn.getType())
{
case 0:
visitInsn(insn.getOpcode());
break;
case 1:
IntInsnNode iinsn = (IntInsnNode)insn;
visitIntInsn(iinsn.getOpcode(), iinsn.operand);
break;
case 2:
VarInsnNode vinsn = (VarInsnNode)insn;
visitVarInsn(vinsn.getOpcode(), vinsn.var);
break;
case 3:
TypeInsnNode tinsn = (TypeInsnNode)insn;
visitTypeInsn(tinsn.getOpcode(), tinsn.desc);
break;
case 4:
FieldInsnNode finsn = (FieldInsnNode)insn;
visitFieldInsn(finsn.getOpcode(), finsn.owner, finsn.name, finsn.desc);
break;
case 5:
MethodInsnNode minsn = (MethodInsnNode)insn;
visitMethodInsn(minsn.getOpcode(), minsn.owner, minsn.name, minsn.desc);
break;
case 6:
InvokeDynamicInsnNode idinsn = (InvokeDynamicInsnNode)insn;
visitInvokeDynamicInsn(idinsn.name, idinsn.desc, idinsn.bsm, idinsn.bsmArgs);
break;
case 7:
JumpInsnNode jinsn = (JumpInsnNode)insn;
visitJumpInsn(jinsn.getOpcode(), jinsn.label.getLabel());
break;
case 8:
LabelNode linsn = (LabelNode)insn;
visitLabel(linsn.getLabel());
break;
case 9:
LdcInsnNode ldcinsn = (LdcInsnNode)insn;
visitLdcInsn(ldcinsn.cst);
break;
case 10:
IincInsnNode iiinsn = (IincInsnNode)insn;
visitIincInsn(iiinsn.var, iiinsn.incr);
break;
case 11:
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode)insn;
Label[] tslables = new Label[tsinsn.labels.size()];
for(int i = 0; i < tslables.length; i++)
tslables[i] = tsinsn.labels.get(i).getLabel();
visitTableSwitchInsn(tsinsn.min, tsinsn.max, tsinsn.dflt.getLabel(), tslables);
break;
case 12:
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode)insn;
Label[] lslables = new Label[lsinsn.labels.size()];
for(int i = 0; i < lslables.length; i++)
lslables[i] = lsinsn.labels.get(i).getLabel();
int[] lskeys = new int[lsinsn.keys.size()];
for(int i = 0; i < lskeys.length; i++)
lskeys[i] = lsinsn.keys.get(i);
visitLookupSwitchInsn(lsinsn.dflt.getLabel(), lskeys, lslables);
break;
case 13:
MultiANewArrayInsnNode ainsn = (MultiANewArrayInsnNode)insn;
visitMultiANewArrayInsn(ainsn.desc, ainsn.dims);
break;
case 14:
FrameNode fnode = (FrameNode)insn;
switch(fnode.type)
{
case -1:
case 0:
visitFrame(fnode.type, fnode.local.size(), fnode.local.toArray(), fnode.stack.size(), fnode.stack.toArray());
break;
case 1:
visitFrame(fnode.type, fnode.local.size(), fnode.local.toArray(), 0, null);
break;
case 2:
visitFrame(fnode.type, fnode.local.size(), null, 0, null);
break;
case 3:
visitFrame(fnode.type, 0, null, 0, null);
break;
case 4:
visitFrame(fnode.type, 0, null, 1, fnode.stack.toArray());
}
break;
case 15:
LineNumberNode lnode = (LineNumberNode)insn;
visitLineNumber(lnode.line, lnode.start.getLabel());
break;
}
}
@Override
public void visitLabel(Label label)
{
if(!buildingLabelMap && !labelNames.containsKey(label))
{
labelNames.put(label, "LEXT"+labelNames.size());
}
super.visitLabel(label);
}
public String textString()
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
print(pw);
pw.flush();
return sw.toString();
}
}
@@ -0,0 +1,241 @@
package codechicken.lib.asm;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
/**
* A section of an InsnList, may become invalid if the insn list is modified
*/
public class InsnListSection implements Iterable<AbstractInsnNode>
{
private class InsnListSectionIterator implements Iterator<AbstractInsnNode>
{
int i = 0;
@Override
public boolean hasNext() {
return i < size();
}
@Override
public AbstractInsnNode next() {
return get(i++);
}
@Override
public void remove() {
InsnListSection.this.remove(--i);
}
}
public InsnList list;
public int start;
public int end;
public InsnListSection(InsnList list, int start, int end) {
this.list = list;
this.start = start;
this.end = end;
}
public InsnListSection(InsnList list, AbstractInsnNode first, AbstractInsnNode last) {
this(list, list.indexOf(first), list.indexOf(last)+1);
}
public InsnListSection(InsnList list) {
this(list, 0, list.size());
}
public InsnListSection() {
this(new InsnList());
}
public void accept(MethodVisitor mv) {
for(AbstractInsnNode insn : this)
insn.accept(mv);
}
public AbstractInsnNode getFirst() {
return size() == 0 ? null : list.get(start);
}
public AbstractInsnNode getLast() {
return size() == 0 ? null : list.get(end - 1);
}
public int size() {
return end - start;
}
public AbstractInsnNode get(int i) {
return list.get(start + i);
}
public void set(int i, AbstractInsnNode insn) {
list.set(get(i), insn);
}
public void remove(int i) {
list.remove(get(i));
end--;
}
public void replace(AbstractInsnNode location, AbstractInsnNode insn) {
list.set(location, insn);
}
public void add(AbstractInsnNode insn) {
list.add(insn);
end++;
}
public void insertBefore(InsnList insns) {
int s = insns.size();
if(this.list.size() == 0)
list.insert(insns);
else
list.insertBefore(list.get(start), insns);
start+=s;
end+=s;
}
public void insert(InsnList insns) {
if(end == 0)
list.insert(insns);
else
list.insert(list.get(end-1), insns);
}
public void replace(InsnList insns) {
int s = insns.size();
remove();
insert(insns);
end = start+s;
}
public void remove() {
while(end != start)
remove(0);
}
public void setLast(AbstractInsnNode last) {
end = list.indexOf(last)+1;
}
public void setFirst(AbstractInsnNode first) {
start = list.indexOf(first);
}
public InsnListSection drop(int n) {
return slice(n, size());
}
public InsnListSection take(int n) {
return slice(0, n);
}
public InsnListSection slice(int start, int end) {
return new InsnListSection(list, this.start+start, this.start+end);
}
/**
* Removes leading and trailing labels and line number nodes that don't affect control flow
* @return this
*/
public InsnListSection trim(Set<LabelNode> controlFlowLabels) {
while(start < end && !InsnComparator.insnImportant(getFirst(), controlFlowLabels))
start++;
while(start < end && !InsnComparator.insnImportant(getLast(), controlFlowLabels))
end--;
return this;
}
public String toString() {
Textifier t = new Textifier();
accept(new TraceMethodVisitor(t));
StringWriter sw = new StringWriter();
t.print(new PrintWriter(sw));
return sw.toString();
}
public void println() {
System.out.println(toString());
}
public HashMap<LabelNode,LabelNode> identityLabelMap() {
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
for (AbstractInsnNode insn : this)
switch(insn.getType()) {
case LABEL:
labelMap.put((LabelNode) insn, (LabelNode) insn);
break;
case JUMP_INSN:
labelMap.put(((JumpInsnNode)insn).label, ((JumpInsnNode)insn).label);
break;
case LOOKUPSWITCH_INSN:
LookupSwitchInsnNode linsn = (LookupSwitchInsnNode)insn;
labelMap.put(linsn.dflt, linsn.dflt);
for(LabelNode label : linsn.labels)
labelMap.put(label, label);
break;
case TABLESWITCH_INSN:
TableSwitchInsnNode tinsn = (TableSwitchInsnNode)insn;
labelMap.put(tinsn.dflt, tinsn.dflt);
for(LabelNode label : tinsn.labels)
labelMap.put(label, label);
break;
case FRAME:
FrameNode fnode = (FrameNode)insn;
if(fnode.local != null)
for(Object o : fnode.local)
if(o instanceof LabelNode)
labelMap.put((LabelNode) o, (LabelNode) o);
if(fnode.stack != null)
for(Object o : fnode.stack)
if(o instanceof LabelNode)
labelMap.put((LabelNode) o, (LabelNode) o);
break;
}
return labelMap;
}
public Map<LabelNode, LabelNode> cloneLabels() {
Map<LabelNode, LabelNode> labelMap = identityLabelMap();
for(Entry<LabelNode, LabelNode> entry : labelMap.entrySet())
entry.setValue(new LabelNode());
return labelMap;
}
public InsnListSection copy() {
return copy(cloneLabels());
}
public InsnListSection copy(Map<LabelNode, LabelNode> labelMap) {
InsnListSection copy = new InsnListSection();
for(AbstractInsnNode insn : this)
copy.add(insn.clone(labelMap));
return copy;
}
@Override
public Iterator<AbstractInsnNode> iterator() {
return new InsnListSectionIterator();
}
}
@@ -1,239 +0,0 @@
package codechicken.lib.asm;
import org.objectweb.asm.tree.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
public class InstructionComparator
{
public static class InsnListSection
{
public InsnListSection(AbstractInsnNode first, AbstractInsnNode last)
{
this.first = first;
this.last = last;
}
public InsnListSection(InsnList haystack, int start, int end)
{
this(haystack.get(start), haystack.get(end));
}
public AbstractInsnNode first;
public AbstractInsnNode last;
}
public static boolean varInsnEqual(VarInsnNode insn1, VarInsnNode insn2)
{
if(insn1.var == -1 || insn2.var == -1)
return true;
return insn1.var == insn2.var;
}
public static boolean methodInsnEqual(AbstractInsnNode absnode, int Opcode, ObfMapping method)
{
if(!(absnode instanceof MethodInsnNode) || absnode.getOpcode() != Opcode)
return false;
return method.matches((MethodInsnNode)absnode);
}
public static boolean methodInsnEqual(MethodInsnNode insn1, MethodInsnNode insn2)
{
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean fieldInsnEqual(FieldInsnNode insn1, FieldInsnNode insn2)
{
return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc);
}
public static boolean ldcInsnEqual(LdcInsnNode insn1, LdcInsnNode insn2)
{
if(insn1.cst.equals("~") || insn2.cst.equals("~"))
return true;
return insn1.cst.equals(insn2.cst);
}
public static boolean typeInsnEqual(TypeInsnNode insn1, TypeInsnNode insn2)
{
if(insn1.desc.equals("~") || insn2.desc.equals("~"))
return true;
return insn1.desc.equals(insn2.desc);
}
public static boolean iincInsnEqual(IincInsnNode node1, IincInsnNode node2)
{
return node1.var == node2.var && node1.incr == node2.incr;
}
public static boolean intInsnEqual(IntInsnNode node1, IntInsnNode node2)
{
if(node1.operand == -1 || node2.operand == -1)
return true;
return node1.operand == node2.operand;
}
public static boolean insnEqual(AbstractInsnNode node1, AbstractInsnNode node2)
{
if(node1.getOpcode() != node2.getOpcode())
return false;
switch(node2.getType())
{
case VAR_INSN:
return varInsnEqual((VarInsnNode) node1, (VarInsnNode) node2);
case TYPE_INSN:
return typeInsnEqual((TypeInsnNode) node1, (TypeInsnNode) node2);
case FIELD_INSN:
return fieldInsnEqual((FieldInsnNode) node1, (FieldInsnNode) node2);
case METHOD_INSN:
return methodInsnEqual((MethodInsnNode) node1, (MethodInsnNode) node2);
case LDC_INSN:
return ldcInsnEqual((LdcInsnNode) node1, (LdcInsnNode) node2);
case IINC_INSN:
return iincInsnEqual((IincInsnNode) node1, (IincInsnNode) node2);
case INT_INSN:
return intInsnEqual((IntInsnNode)node1, (IntInsnNode)node2);
default:
return true;
}
}
public static InsnList getImportantList(InsnList list)
{
if(list.size() == 0)
return list;
HashMap<LabelNode, LabelNode> labels = new HashMap<LabelNode, LabelNode>();
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
{
if(insn instanceof LabelNode)
labels.put((LabelNode)insn, (LabelNode)insn);
}
InsnList importantNodeList = new InsnList();
for(AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext())
{
if(insn instanceof LabelNode || insn instanceof LineNumberNode)
continue;
importantNodeList.add(insn.clone(labels));
}
return importantNodeList;
}
public static boolean insnListMatches(InsnList haystack, InsnList needle, int start)
{
if(haystack.size()-start < needle.size())
return false;
for(int i = 0; i < needle.size(); i++)
{
if(!insnEqual(haystack.get(i+start), needle.get(i)))
return false;
}
return true;
}
public static List<Integer> insnListFind(InsnList haystack, InsnList needle)
{
LinkedList<Integer> list = new LinkedList<Integer>();
for(int start = 0; start <= haystack.size()-needle.size(); start++)
if(insnListMatches(haystack, needle, start))
list.add(start);
return list;
}
public static List<AbstractInsnNode> insnListFindStart(InsnList haystack, InsnList needle)
{
LinkedList<AbstractInsnNode> callNodes = new LinkedList<AbstractInsnNode>();
for(int callPoint : insnListFind(haystack, needle))
callNodes.add(haystack.get(callPoint));
return callNodes;
}
public static List<AbstractInsnNode> insnListFindEnd(InsnList haystack, InsnList needle)
{
LinkedList<AbstractInsnNode> callNodes = new LinkedList<AbstractInsnNode>();
for(int callPoint : insnListFind(haystack, needle))
callNodes.add(haystack.get(callPoint+needle.size()-1));
return callNodes;
}
public static List<InsnListSection> insnListFindL(InsnList haystack, InsnList needle)
{
HashSet<LabelNode> controlFlowLabels = new HashSet<LabelNode>();
for(AbstractInsnNode insn = haystack.getFirst(); insn != null; insn = insn.getNext())
{
switch(insn.getType())
{
case 8:
case 15:
break;
case 7:
JumpInsnNode jinsn = (JumpInsnNode)insn;
controlFlowLabels.add(jinsn.label);
break;
case 11:
TableSwitchInsnNode tsinsn = (TableSwitchInsnNode)insn;
for(LabelNode label : tsinsn.labels)
controlFlowLabels.add(label);
break;
case 12:
LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode)insn;
for(LabelNode label : lsinsn.labels)
controlFlowLabels.add(label);
break;
}
}
LinkedList<InsnListSection> list = new LinkedList<InsnListSection>();
nextsection: for(int start = 0; start <= haystack.size()-needle.size(); start++)
{
InsnListSection section = insnListMatchesL(haystack, needle, start, controlFlowLabels);
if(section != null)
{
for(InsnListSection asection : list)
if(asection.last == section.last)
continue nextsection;
list.add(section);
}
}
return list;
}
private static InsnListSection insnListMatchesL(InsnList haystack, InsnList needle, int start, HashSet<LabelNode> controlFlowLabels)
{
int h = start, n = 0;
for(;h < haystack.size() && n < needle.size(); h++)
{
AbstractInsnNode insn = haystack.get(h);
if(insn.getType() == 15)
continue;
if(insn.getType() == 8 && !controlFlowLabels.contains(insn))
continue;
if(!insnEqual(haystack.get(h), needle.get(n)))
return null;
n++;
}
if(n != needle.size())
return null;
return new InsnListSection(haystack, start, h-1);
}
}
@@ -0,0 +1,37 @@
package codechicken.lib.asm;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.LocalVariablesSorter;
import java.util.Set;
public class LocalVariablesSorterVisitor extends ClassVisitor
{
public Set<ObfMapping> methods;
public String owner;
public LocalVariablesSorterVisitor(Set<ObfMapping> methods, ClassVisitor cv) {
super(Opcodes.ASM4, cv);
this.methods = methods;
}
public LocalVariablesSorterVisitor(ClassVisitor cv) {
this(null, cv);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
owner = name;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return methods == null || methods.contains(new ObfMapping(owner, name, desc)) ? new LocalVariablesSorter(access, desc, mv) : mv;
}
}
@@ -0,0 +1,285 @@
package codechicken.lib.asm;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import java.io.File;
import java.util.*;
import static codechicken.lib.asm.ASMHelper.*;
public class ModularASMTransformer
{
public static class ClassNodeTransformerList
{
List<ClassNodeTransformer> transformers = new LinkedList<ClassNodeTransformer>();
HashSet<ObfMapping> methodsToSort = new HashSet<ObfMapping>();
public void add(ClassNodeTransformer t) {
transformers.add(t);
t.addMethodsToSort(methodsToSort);
}
public byte[] transform(byte[] bytes) {
ClassNode cnode = new ClassNode();
ClassReader reader = new ClassReader(bytes);
ClassVisitor cv = cnode;
if(!methodsToSort.isEmpty())
cv = new LocalVariablesSorterVisitor(methodsToSort, cv);
reader.accept(cv, ClassReader.EXPAND_FRAMES);
try {
int writeFlags = 0;
for(ClassNodeTransformer t : transformers) {
t.transform(cnode);
writeFlags|=t.writeFlags;
}
bytes = createBytes(cnode, writeFlags);
if(config.getTag("dump_asm").getBooleanValue(true))
dump(bytes, new File("asm/ccl_modular/"+cnode.name.replace('/', '#')+".txt"), false, false);
return bytes;
} catch (RuntimeException e) {
dump(bytes, new File("asm/ccl_modular/"+cnode.name.replace('/', '#')+".txt"), false, false);
throw e;
}
}
}
public static abstract class ClassNodeTransformer
{
public int writeFlags;
public ClassNodeTransformer(int writeFlags) {
this.writeFlags = writeFlags;
}
public ClassNodeTransformer() {
this(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
}
public abstract String className();
public abstract void transform(ClassNode cnode);
public void addMethodsToSort(Set<ObfMapping> set) {}
}
public static abstract class MethodTransformer extends ClassNodeTransformer
{
public final ObfMapping method;
public MethodTransformer(ObfMapping method) {
this.method = method.toClassloading();
}
@Override
public String className() {
return method.javaClass();
}
@Override
public void transform(ClassNode cnode) {
MethodNode mv = findMethod(method, cnode);
if (mv == null)
throw new RuntimeException("Method not found: " + method);
try {
transform(mv);
} catch (Exception e) {
throw new RuntimeException("Error transforming method: "+method, e);
}
}
public abstract void transform(MethodNode mv);
}
public static class MethodWriter extends ClassNodeTransformer
{
public final int access;
public final ObfMapping method;
public final String[] exceptions;
public InsnList list;
public MethodWriter(int access, ObfMapping method) {
this(access, method, null, (InsnList)null);
}
public MethodWriter(int access, ObfMapping method, InsnList list) {
this(access, method, null, list);
}
public MethodWriter(int access, ObfMapping method, ASMBlock block) {
this(access, method, null, block);
}
public MethodWriter(int access, ObfMapping method, String[] exceptions) {
this(access, method, exceptions, (InsnList)null);
}
public MethodWriter(int access, ObfMapping method, String[] exceptions, InsnList list) {
this.access = access;
this.method = method.toClassloading();
this.exceptions = exceptions;
this.list = list;
}
public MethodWriter(int access, ObfMapping method, String[] exceptions, ASMBlock block) {
this(access, method, exceptions, block.rawListCopy());
}
@Override
public String className() {
return method.javaClass();
}
@Override
public void transform(ClassNode cnode) {
MethodNode mv = findMethod(method, cnode);
if(mv == null)
mv = (MethodNode) method.visitMethod(cnode, access, exceptions);
else {
mv.access = access;
mv.instructions.clear();
if (mv.localVariables != null)
mv.localVariables.clear();
if (mv.tryCatchBlocks != null)
mv.tryCatchBlocks.clear();
}
write(mv);
}
public void write(MethodNode mv) {
logger.debug("Writing method "+method);
list.accept(mv);
}
}
public static class MethodInjector extends MethodTransformer
{
public ASMBlock needle;
public ASMBlock injection;
public boolean before;
public MethodInjector(ObfMapping method, ASMBlock needle, ASMBlock injection, boolean before) {
super(method);
this.needle = needle;
this.injection = injection;
this.before = before;
}
public MethodInjector(ObfMapping method, ASMBlock injection, boolean before) {
this(method, null, injection, before);
}
public MethodInjector(ObfMapping method, InsnList needle, InsnList injection, boolean before) {
this(method, new ASMBlock(needle), new ASMBlock(injection), before);
}
public MethodInjector(ObfMapping method, InsnList injection, boolean before) {
this(method, null, new ASMBlock(injection), before);
}
@Override
public void addMethodsToSort(Set<ObfMapping> set) {
set.add(method);
}
@Override
public void transform(MethodNode mv) {
if(needle == null) {
logger.debug("Injecting "+(before ? "before" : "after")+" method "+method);
if (before)
mv.instructions.insert(injection.rawListCopy());
else
mv.instructions.add(injection.rawListCopy());
}
else {
for (InsnListSection key : InsnComparator.findN(mv.instructions, needle.list)) {
logger.debug("Injecting "+(before ? "before" : "after")+" method "+method+" @ "+key.start+" - "+key.end);
ASMBlock injectBlock = injection.copy().mergeLabels(needle.applyLabels(key));
if (before)
key.insertBefore(injectBlock.list.list);
else
key.insert(injectBlock.list.list);
}
}
}
}
public static class MethodReplacer extends MethodTransformer
{
public ASMBlock needle;
public ASMBlock replacement;
public MethodReplacer(ObfMapping method, ASMBlock needle, ASMBlock replacement) {
super(method);
this.needle = needle;
this.replacement = replacement;
}
public MethodReplacer(ObfMapping method, InsnList needle, InsnList replacement) {
this(method, new ASMBlock(needle), new ASMBlock(replacement));
}
@Override
public void addMethodsToSort(Set<ObfMapping> set) {
set.add(method);
}
@Override
public void transform(MethodNode mv) {
for (InsnListSection key : InsnComparator.findN(mv.instructions, needle.list)) {
logger.debug("Replacing method "+method+" @ "+key.start+" - "+key.end);
ASMBlock replaceBlock = replacement.copy().pullLabels(needle.applyLabels(key));
key.insert(replaceBlock.list.list);
}
}
}
public static class FieldWriter extends ClassNodeTransformer
{
public final ObfMapping field;
public final int access;
public final Object value;
public FieldWriter(int access, ObfMapping field, Object value) {
this.field = field.toClassloading();
this.access = access;
this.value = value;
}
public FieldWriter(int access, ObfMapping field) {
this(access, field, null);
}
@Override
public String className() {
return field.javaClass();
}
@Override
public void transform(ClassNode cnode) {
field.visitField(cnode, access, value);
}
}
public HashMap<String, ClassNodeTransformerList> transformers = new HashMap<String, ClassNodeTransformerList>();
public void add(ClassNodeTransformer t) {
ClassNodeTransformerList list = transformers.get(t.className());
if(list == null)
transformers.put(t.className(), list = new ClassNodeTransformerList());
list.add(t);
}
public byte[] transform(String name, byte[] bytes) {
if(bytes == null)
return null;
ClassNodeTransformerList list = transformers.get(name);
return list == null ? bytes : list.transform(bytes);
}
}
+19 -9
View File
@@ -1,6 +1,5 @@
package codechicken.lib.asm;
import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.ConfigTag;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
@@ -9,6 +8,9 @@ import com.google.common.io.Resources;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
import cpw.mods.fml.relauncher.FMLInjectionData;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.common.ForgeVersion;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.tree.*;
@@ -74,14 +76,9 @@ public class ObfMapping
public static class MCPRemapper extends Remapper implements LineProcessor<Void>
{
public static ConfigFile config;
public static File[] getConfFiles() {
if (config == null)
config = new ConfigFile(new File((File) FMLInjectionData.data()[6], "config/CodeChickenLib.cfg")).setComment("CodeChickenLib configuration file.");
ConfigTag tag = config.getTag("dev.mappingDir").setComment("Path to directory holding packaged.srg, fields.csv and methods.csv for mcp remapping");
for (int i = 0; i < 6; i++) {
ConfigTag tag = ASMHelper.config.getTag("mappingDir").setComment("Path to directory holding packaged.srg, fields.csv and methods.csv for mcp remapping");
for (int i = 0; i < DIR_GUESSES+DIR_ASKS; i++) {
File dir = confDirectoryGuess(i, tag);
if (dir == null || dir.isFile())
continue;
@@ -90,7 +87,7 @@ public class ObfMapping
try {
mappings = parseConfDir(dir);
} catch (Exception e) {
if (i >= 3)
if (i >= DIR_GUESSES)
e.printStackTrace();
continue;
}
@@ -102,6 +99,8 @@ public class ObfMapping
throw new RuntimeException("Failed to select mappings directory, set it manually in the config");
}
private static final int DIR_GUESSES = 4;
private static final int DIR_ASKS = 3;
public static File confDirectoryGuess(int i, ConfigTag tag) {
File mcDir = (File) FMLInjectionData.data()[6];
switch (i) {
@@ -111,6 +110,9 @@ public class ObfMapping
return new File(mcDir, "../conf");
case 2:
return new File(mcDir, "../build/unpacked/conf");
case 3:
return new File(System.getProperty("user.home"), ".gradle/caches/minecraft/net/minecraftforge/forge/"+
FMLInjectionData.data()[4]+"-"+ ForgeVersion.getVersion()+"/unpacked/conf");
default:
JFileChooser fc = new JFileChooser(mcDir);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
@@ -281,6 +283,14 @@ public class ObfMapping
mv.visitFieldInsn(opcode, s_owner, s_name, s_desc);
}
public MethodVisitor visitMethod(ClassVisitor visitor, int access, String[] exceptions) {
return visitor.visitMethod(access, s_name, s_desc, null, exceptions);
}
public FieldVisitor visitField(ClassVisitor visitor, int access, Object value) {
return visitor.visitField(access, s_name, s_desc, null, value);
}
public boolean isClass(String name) {
return name.replace('.', '/').equals(s_owner);
}
+14 -23
View File
@@ -4,7 +4,19 @@ import java.io.*;
public class ConfigFile extends ConfigTagParent
{
public static final byte[] crlf = new byte[]{0xD, 0xA};
public File file;
private boolean loading;
public ConfigFile(File file) {
newlinemode = 2;
load(file);
}
protected ConfigFile() {}
protected void load(File file) {
try {
if(!file.getParentFile().exists())
file.getParentFile().mkdirs();
@@ -14,11 +26,10 @@ public class ConfigFile extends ConfigTagParent
throw new RuntimeException(e);
}
this.file = file;
newlinemode = 2;
loadConfig();
}
private void loadConfig() {
protected void loadConfig() {
loading = true;
BufferedReader reader;
try {
@@ -66,22 +77,7 @@ public class ConfigFile extends ConfigTagParent
public static String readLine(BufferedReader reader) throws IOException {
String line = reader.readLine();
if (line != null)
return line.replace("\t", "");
return line;
}
public static String formatLine(String line) {
line = line.replace("\t", "");
if (line.startsWith("#")) {
return line;
} else if (line.contains("=")) {
line = line.substring(0, line.indexOf("=")).replace(" ", "") + line.substring(line.indexOf("="));
return line;
} else {
line = line.replace(" ", "");
return line;
}
return line == null ? null : line.replace("\t", "");
}
public static void writeLine(PrintWriter writer, String line, int tabs) {
@@ -112,9 +108,4 @@ public class ConfigFile extends ConfigTagParent
public boolean isLoading() {
return loading;
}
public File file;
private boolean loading;
public static final byte[] lineend = new byte[]{0xD, 0xA};
}
@@ -182,7 +182,7 @@ public abstract class ConfigTagParent
break;
if(line.startsWith("#"))
{
if(comment == null || comment.equals(""))
if(comment.equals(""))
comment = line.substring(1);
else
comment = comment+"\n"+line.substring(1);
@@ -0,0 +1,18 @@
package codechicken.lib.config;
import java.io.File;
public class DefaultingConfigFile extends ConfigFile
{
public DefaultingConfigFile(File file) {
super();
if(file.exists())
load(file);
}
@Override
public void saveConfig() {
if(file != null)
super.saveConfig();
}
}