mirror of
https://github.com/GTNewHorizons/CodeChickenCore.git
synced 2026-11-11 07:47:47 +00:00
Upgraded ASM lib
This commit is contained in:
1 parent
0a14b64e12
commit
52f82017e7
14 files changed
+1142
-747
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user