TabooLib/src/main/scala/io/izzel/taboolib/util/asm/AsmClassTransformer.java

164 lines
5.7 KiB
Java

package io.izzel.taboolib.util.asm;
import io.izzel.taboolib.util.Ref;
import org.bukkit.Bukkit;
import org.objectweb.asm.*;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
public class AsmClassTransformer extends ClassVisitor implements Opcodes {
private final Class<?> from;
private final String fromVer;
private final String toVer;
private final ClassWriter writer;
private String newClassName;
private String prevName;
private AsmClassTransformer(Class<?> from, String fromVer, String toVer, ClassWriter classWriter) {
super(Opcodes.ASM5, classWriter);
this.writer = classWriter;
this.from = from;
this.fromVer = fromVer;
this.toVer = toVer;
}
public Object transform() {
try {
ClassReader classReader = new ClassReader(from.getResourceAsStream("/" + from.getName().replace('.', '/') + ".class"));
newClassName = from.getName() + "_TabooLibRemap_" + this.hashCode() + "_" + toVer;
prevName = from.getName().replace('.', '/');
classReader.accept(this, ClassReader.SKIP_CODE);
Class<?> clazz = AsmClassLoader.createNewClass(newClassName, writer.toByteArray());
Field field = from.getClassLoader().getClass().getDeclaredField("classes");
field.setAccessible(true);
Ref.getField(from.getClassLoader(), field, Map.class).put(newClassName, clazz);
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (IOException | NoSuchFieldException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
public static Builder builder() {
return new Builder().toVersion(Bukkit.getServer().getClass().getName().split("\\.")[3]);
}
public static Builder builder(String ver) {
return new Builder().toVersion(ver);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor visitor = super.visitMethod(access, name, replace(descriptor), replace(signature), replace(exceptions));
return new AsmMethodTransformer(visitor);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
return super.visitField(access, name, replace(descriptor), replace(signature), value);
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
super.visitInnerClass(replace(name), outerName, replace(name).substring(outerName.length() + 1), access);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, newClassName.replace('.', '/'), replace(signature), replace(superName), replace(interfaces));
}
private String replace(String text) {
if (text != null) {
return text
.replace("net/minecraft/server/" + fromVer, "net/minecraft/server/" + toVer)
.replace("org/bukkit/craftbukkit/" + fromVer, "org/bukkit/craftbukkit/" + toVer)
.replace(prevName, newClassName.replace('.', '/'));
} else {
return null;
}
}
private String[] replace(String[] text) {
if (text != null) {
for (int i = 0; i < text.length; i++) {
text[i] = replace(text[i]);
}
return text;
} else {
return null;
}
}
private class AsmMethodTransformer extends MethodVisitor {
AsmMethodTransformer(MethodVisitor visitor) {
super(Opcodes.ASM5, visitor);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
super.visitMethodInsn(opcode, replace(owner), name, replace(descriptor), isInterface);
}
@Override
public void visitLdcInsn(Object value) {
if (value instanceof String) {
super.visitLdcInsn(replace((String) value));
} else {
super.visitLdcInsn(value);
}
}
@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, replace(type));
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
super.visitFieldInsn(opcode, replace(owner), name, replace(descriptor));
}
@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
super.visitLocalVariable(name, replace(descriptor), replace(signature), start, end, index);
}
}
public static class Builder {
private Class<?> from;
private String fromVersion, toVersion;
public Builder from(Class<?> clazz) {
this.from = clazz;
return this;
}
public Builder fromVersion(String ver) {
fromVersion = ver;
return this;
}
public Builder toVersion(String ver) {
toVersion = ver;
return this;
}
public AsmClassTransformer build() {
return new AsmClassTransformer(from, fromVersion, toVersion, new ClassWriter(ClassWriter.COMPUTE_MAXS));
}
}
}