package com.googlecode.totallylazy.reflection;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.predicates.Predicates;
import com.googlecode.totallylazy.Sequence;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
import jdk.internal.org.objectweb.asm.tree.FieldInsnNode;
import jdk.internal.org.objectweb.asm.tree.FieldNode;
import jdk.internal.org.objectweb.asm.tree.InsnNode;
import jdk.internal.org.objectweb.asm.tree.LocalVariableNode;
import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
import jdk.internal.org.objectweb.asm.tree.TypeInsnNode;
import jdk.internal.org.objectweb.asm.tree.VarInsnNode;
import java.util.NoSuchElementException;
import static com.googlecode.totallylazy.Option.none;
import static com.googlecode.totallylazy.Option.some;
import static com.googlecode.totallylazy.functions.Functions.instanceOf;
import static com.googlecode.totallylazy.predicates.Predicates.not;
public class Declaration {
private final String name;
private final java.lang.reflect.Type type;
public Declaration(String name, java.lang.reflect.Type type) {
this.name = name;
this.type = type;
}
public String name() {
return name;
}
public java.lang.reflect.Type type() {
return type;
}
public <T> Class<? extends T> forClass() {
return Types.classOf(type);
}
public static Declaration declaration() {
return declaration(StackFrames.stackFrames().tail()).get();
}
private static Option<Declaration> declaration(Sequence<StackFrame> stackFrame) {
StackFrame callee = stackFrame.first();
StackFrame caller = stackFrame.second();
Sequence<AbstractInsnNode> instructions = caller.instructions().dropWhile(not(Predicates.instanceOf(MethodInsnNode.class,
insn -> insn.owner.equals(callee.classNode().name) &&
insn.desc.equals(callee.methodNode().desc) &&
insn.name.equals(callee.methodNode().name)))).tail().realise();
AbstractInsnNode nextInstruction = instructions.reject(Predicates.instanceOf(TypeInsnNode.class)).head();
if(nextInstruction instanceof VarInsnNode){
VarInsnNode node = (VarInsnNode) nextInstruction;
LocalVariableNode variable = Asm.localVariables(caller.methodNode()).filter(v -> v.index == node.var).head();
return some(new Declaration(variable.name, Signature.parse(variable.signature == null ? variable.desc : variable.signature, callee.aClass())));
}
if(nextInstruction instanceof FieldInsnNode) {
FieldInsnNode node = (FieldInsnNode) nextInstruction;
FieldNode field = Asm.fields(caller.classNode()).filter(f -> f.name.equals(node.name)).head();
return some(new Declaration(field.name, Signature.parse(field.signature == null ? field.desc : field.signature, callee.aClass())));
}
if(nextInstruction instanceof InsnNode){
InsnNode node = (InsnNode) nextInstruction;
if(node.getOpcode() == Opcodes.ARETURN){
return declaration(stackFrame.tail()).orElse(() -> some(new Declaration("{anonymous}", caller.method().getGenericReturnType())));
}
}
return none();
}
}