2013-12-04 19:19:02 +01:00
|
|
|
package com.comphenix.protocol.reflect;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.util.List;
|
|
|
|
|
2014-11-15 19:02:03 +01:00
|
|
|
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
|
|
|
|
import com.google.common.collect.Lists;
|
|
|
|
|
2017-06-14 21:41:00 +02:00
|
|
|
import net.sf.cglib.asm.*;
|
|
|
|
|
2015-06-17 20:25:39 +02:00
|
|
|
public class ClassAnalyser {
|
2013-12-04 19:19:02 +01:00
|
|
|
/**
|
|
|
|
* Represents a method in ASM.
|
2014-01-09 20:01:37 +01:00
|
|
|
* <p>
|
|
|
|
* Keep in mind that this may also invoke a constructor.
|
2013-12-04 19:19:02 +01:00
|
|
|
* @author Kristian
|
|
|
|
*/
|
|
|
|
public static class AsmMethod {
|
2014-01-09 20:01:37 +01:00
|
|
|
public enum AsmOpcodes {
|
|
|
|
INVOKE_VIRTUAL,
|
|
|
|
INVOKE_SPECIAL,
|
|
|
|
INVOKE_STATIC,
|
|
|
|
INVOKE_INTERFACE,
|
|
|
|
INVOKE_DYNAMIC;
|
|
|
|
|
|
|
|
public static AsmOpcodes fromIntOpcode(int opcode) {
|
|
|
|
switch (opcode) {
|
2017-06-14 21:41:00 +02:00
|
|
|
case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
|
|
|
|
case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
|
|
|
|
case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
|
|
|
|
case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
|
|
|
|
case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
|
2014-01-09 20:01:37 +01:00
|
|
|
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private final AsmOpcodes opcode;
|
2013-12-04 19:19:02 +01:00
|
|
|
private final String ownerClass;
|
|
|
|
private final String methodName;
|
|
|
|
private final String signature;
|
|
|
|
|
2014-01-09 20:01:37 +01:00
|
|
|
public AsmMethod(AsmOpcodes opcode, String ownerClass, String methodName, String signature) {
|
|
|
|
this.opcode = opcode;
|
2013-12-04 19:19:02 +01:00
|
|
|
this.ownerClass = ownerClass;
|
|
|
|
this.methodName = methodName;
|
|
|
|
this.signature = signature;
|
|
|
|
}
|
|
|
|
|
2013-12-08 23:45:35 +01:00
|
|
|
public String getOwnerName() {
|
2013-12-04 19:19:02 +01:00
|
|
|
return ownerClass;
|
|
|
|
}
|
|
|
|
|
2014-01-09 20:01:37 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the opcode used to invoke this method or constructor.
|
|
|
|
* @return The opcode.
|
|
|
|
*/
|
|
|
|
public AsmOpcodes getOpcode() {
|
|
|
|
return opcode;
|
|
|
|
}
|
|
|
|
|
2013-12-08 23:45:35 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the associated owner class.
|
|
|
|
* @return The owner class.
|
2015-06-17 20:25:39 +02:00
|
|
|
* @throws ClassNotFoundException If the class was not found
|
2013-12-08 23:45:35 +01:00
|
|
|
*/
|
|
|
|
public Class<?> getOwnerClass() throws ClassNotFoundException {
|
|
|
|
return AsmMethod.class.getClassLoader().loadClass(getOwnerName().replace('/', '.'));
|
|
|
|
}
|
|
|
|
|
2013-12-04 19:19:02 +01:00
|
|
|
public String getMethodName() {
|
|
|
|
return methodName;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getSignature() {
|
|
|
|
return signature;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private static final ClassAnalyser DEFAULT = new ClassAnalyser();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the default instance.
|
|
|
|
* @return The default.
|
|
|
|
*/
|
|
|
|
public static ClassAnalyser getDefault() {
|
|
|
|
return DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve every method calls in the given method.
|
|
|
|
* @param method - the method to analyse.
|
|
|
|
* @return The method calls.
|
|
|
|
* @throws IOException Cannot access the parent class.
|
|
|
|
*/
|
|
|
|
public List<AsmMethod> getMethodCalls(Method method) throws IOException {
|
2013-12-08 23:45:35 +01:00
|
|
|
return getMethodCalls(method.getDeclaringClass(), method);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve every method calls in the given method.
|
|
|
|
* @param clazz - the parent class.
|
|
|
|
* @param method - the method to analyse.
|
|
|
|
* @return The method calls.
|
|
|
|
* @throws IOException Cannot access the parent class.
|
|
|
|
*/
|
2017-06-14 21:41:00 +02:00
|
|
|
private List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
|
|
|
|
final $ClassReader reader = new $ClassReader(clazz.getCanonicalName());
|
2013-12-04 19:19:02 +01:00
|
|
|
final List<AsmMethod> output = Lists.newArrayList();
|
|
|
|
|
|
|
|
// The method we are looking for
|
|
|
|
final String methodName = method.getName();
|
2017-06-14 21:41:00 +02:00
|
|
|
final String methodDescription = $Type.getMethodDescriptor(method);
|
|
|
|
|
|
|
|
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
|
2013-12-04 19:19:02 +01:00
|
|
|
@Override
|
2017-06-14 21:41:00 +02:00
|
|
|
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
2013-12-04 19:19:02 +01:00
|
|
|
if (methodName.equals(name) && methodDescription.equals(desc)) {
|
2017-06-14 21:41:00 +02:00
|
|
|
return new $MethodVisitor($Opcodes.ASM5) {
|
2013-12-04 19:19:02 +01:00
|
|
|
@Override
|
2017-06-14 21:41:00 +02:00
|
|
|
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) {
|
2014-01-09 20:01:37 +01:00
|
|
|
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
|
2013-12-04 19:19:02 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2017-06-14 21:41:00 +02:00
|
|
|
|
2013-12-04 19:19:02 +01:00
|
|
|
return null;
|
|
|
|
}
|
2017-06-14 21:41:00 +02:00
|
|
|
}, $ClassReader.EXPAND_FRAMES);
|
2013-12-04 19:19:02 +01:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
}
|