package com.googlecode.totallylazy.reflection;
import com.googlecode.totallylazy.Bytes;
import com.googlecode.totallylazy.Sequence;
import com.googlecode.totallylazy.Unchecked;
import com.googlecode.totallylazy.iterators.ReadOnlyIterator;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
import jdk.internal.org.objectweb.asm.tree.ClassNode;
import jdk.internal.org.objectweb.asm.tree.FieldNode;
import jdk.internal.org.objectweb.asm.tree.InsnList;
import jdk.internal.org.objectweb.asm.tree.LocalVariableNode;
import jdk.internal.org.objectweb.asm.tree.MethodNode;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
import static com.googlecode.totallylazy.Sequences.sequence;
public class Asm {
private static final Map<Class<?>, WeakReference<ClassNode>> cache = Collections.synchronizedMap(new WeakHashMap<>());
public static ClassNode classNode(final Class<?> aClass) {
ClassNode classNode = cache.computeIfAbsent(aClass, c -> new WeakReference<>(create(c))).get();
if(classNode == null) {
classNode = create(aClass);
cache.put(aClass, new WeakReference<>(classNode));
}
return classNode;
}
private static ClassNode create(Class<?> c) {
return classNode(Bytes.bytes(c));
}
public static ClassNode classNode(final byte[] bytes) {
ClassReader reader = new ClassReader(bytes);
ClassNode classNode = new ClassNode();
reader.accept(classNode, 0);
return classNode;
}
public static Sequence<MethodNode> methods(ClassNode classNode) {
return sequence(Unchecked.<List<MethodNode>>cast(classNode.methods));
}
public static Sequence<AbstractInsnNode> instructions(MethodNode method) {
return instructions(method.instructions);
}
public static Sequence<AbstractInsnNode> instructions(final InsnList instructions) {
return new Sequence<AbstractInsnNode>() {
@Override
public Iterator<AbstractInsnNode> iterator() {
return new InsnIterator(instructions);
}
};
}
public static Sequence<LocalVariableNode> localVariables(MethodNode methodNode) {
return sequence(methodNode.localVariables);
}
public static Sequence<FieldNode> fields(ClassNode classNode) {
return sequence(classNode.fields);
}
public static class InsnIterator extends ReadOnlyIterator<AbstractInsnNode> {
private final InsnList list;
private int index = 0;
public InsnIterator(final InsnList list) {
this.list = list;
}
public final boolean hasNext() {
return index < list.size();
}
public final AbstractInsnNode next() {
if (hasNext()) {
return list.get(index++);
}
throw new NoSuchElementException();
}
}
public static Type[] getArgumentTypes(Constructor<?> constructor) {
Class[] var1 = constructor.getParameterTypes();
Type[] var2 = new Type[var1.length];
for(int var3 = var1.length - 1; var3 >= 0; --var3) {
var2[var3] = Type.getType(var1[var3]);
}
return var2;
}
public static int store(Class<?> aClass) {
return Type.getType(aClass).getOpcode(Opcodes.ISTORE);
}
public static int load(Class<?> aClass) {
return Type.getType(aClass).getOpcode(Opcodes.ILOAD);
}
public static int returns(Class<?> aClass) {
return Type.getType(aClass).getOpcode(Opcodes.IRETURN);
}
}