package com.rwtema.funkylocomotion.asm;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.rwtema.funkylocomotion.helper.ItemHelper;
import com.rwtema.funkylocomotion.items.ItemWrench;
import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import static org.objectweb.asm.Opcodes.*;
public class WrenchFactory {
private static LaunchClassLoader loader = (LaunchClassLoader) ItemWrench.class.getClassLoader();
public static ItemWrench makeMeAWrench() {
ArrayList<ClassNode> nodes = new ArrayList<>(ItemHelper.wrenchClassNames.length);
ArrayList<String> ifaceList = new ArrayList<>(ItemHelper.wrenchClassNames.length);
LinkedList<String> toCheck = Lists.newLinkedList();
Collections.addAll(toCheck, ItemHelper.wrenchClassNames);
while (!toCheck.isEmpty()) {
try {
String wrenchClassName = toCheck.poll();
byte[] classBytes = loader.getClassBytes(wrenchClassName);
if (classBytes != null) {
ClassNode node = new ClassNode(ASM5);
ClassReader reader = new ClassReader(classBytes);
reader.accept(node, ClassReader.EXPAND_FRAMES);
for (String anInterface : node.interfaces) {
toCheck.add(anInterface.replace('/', '.'));
}
nodes.add(node);
ifaceList.add(wrenchClassName.replace('.', '/'));
}
} catch (IOException ignore) {
}
}
if (nodes.isEmpty()) return new ItemWrench();
HashSet<String> methods = new HashSet<>();
try {
byte[] classBytes = loader.getClassBytes(ItemWrench.class.getName());
ClassNode node = new ClassNode(ASM5);
ClassReader reader = new ClassReader(classBytes);
reader.accept(node, ClassReader.EXPAND_FRAMES);
for (MethodNode method : node.methods) {
methods.add(getMethodDesc(method));
}
} catch (IOException ignore) {
}
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
String name = "FLM_ItemWrench";
String superName = Type.getInternalName(ItemWrench.class);
String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]);
cw.visit(V1_6, ACC_PUBLIC | ACC_SUPER, name, null, superName, ifaces);
cw.visitSource(".dynamic", null);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superName, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
for (ClassNode node : nodes) {
for (MethodNode method : node.methods) {
String mn = getMethodDesc(method);
if (methods.contains(mn))
continue;
methods.add(mn);
Type returnType = Type.getReturnType(method.desc);
int returnOpCode = returnType.getOpcode(IRETURN);
mv = cw.visitMethod(ACC_PUBLIC, method.name, method.desc, null, null);
mv.visitCode();
switch (returnOpCode) {
case RETURN:
break;
case IRETURN:
mv.visitInsn(returnType == Type.BOOLEAN_TYPE ? ICONST_1 : ICONST_0);
break;
case LRETURN:
mv.visitInsn(LCONST_0);
break;
case FRETURN:
mv.visitInsn(FCONST_0);
break;
case DRETURN:
mv.visitInsn(DCONST_0);
break;
case ARETURN:
mv.visitInsn(ACONST_NULL);
break;
}
mv.visitInsn(returnOpCode);
mv.visitInsn(RETURN);
mv.visitMaxs(returnOpCode != RETURN ? 1 : 0, 1 + Type.getArgumentTypes(method.desc).length);
mv.visitEnd();
}
}
cw.visitEnd();
Class<?> ret = (new ASMClassLoader()).define(name, cw.toByteArray());
try {
return (ItemWrench) ret.newInstance();
} catch (Throwable e) {
throw Throwables.propagate(e);
}
}
private static String getMethodDesc(MethodNode method) {
return method.name + "_" + method.desc;
}
private static class ASMClassLoader extends ClassLoader {
private ASMClassLoader() {
super(ASMClassLoader.class.getClassLoader());
}
public Class<?> define(String name, byte[] data) {
return defineClass(name, data, 0, data.length);
}
}
}