mirror of
https://github.com/GTNewHorizons/CodeChickenCore.git
synced 2026-11-11 07:47:47 +00:00
285 lines
9.3 KiB
Java
285 lines
9.3 KiB
Java
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);
|
||
|
|
}
|
||
|
|
}
|