Кто-то проголосовал за то, чтобы закрыть этот вопрос как "слишком широкий". Я не уверен, является ли это подходящей близкой причиной здесь, но может быть, потому что можно рассмотреть этот вопрос (который является продолжением ваш предыдущий вопрос), просто попросив других сделать некоторую работу за вас.
Однако, чтобы ответить на основной вопрос как обнаружить ссылки между классами в одном файле JAR с помощью BCEL:
Вы можете получить список JavaClass
объектов из файла JarFile
. Для каждого из этих JavaClass
объектов вы можете просмотреть Method
объекты и их InstructionList
. Из этих инструкций вы можете выбрать InvokeInstruction
объекты и изучить их дальше, чтобы узнать, какой метод для какого класса фактически вызывается там.
Следующая программа открывает JAR-файл (по понятным причинам это bcel-5.2.jar
- он вам все равно понадобится...) и обрабатывает его описанным выше способом. Для каждого JavaClass
файла JAR он создает карту из всех объектов JavaClass
, на которые ссылаются, в список Method
, которые вызываются для этих классов, и соответствующим образом печатает информацию:
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;
public class BCELRelationships
{
public static void main(String[] args) throws Exception
{
JarFile jarFile = null;
try
{
String jarName = "bcel-5.2.jar";
jarFile = new JarFile(jarName);
findReferences(jarName, jarFile);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (jarFile != null)
{
try
{
jarFile.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
private static void findReferences(String jarName, JarFile jarFile)
throws ClassFormatException, IOException, ClassNotFoundException
{
Map<String, JavaClass> javaClasses =
collectJavaClasses(jarName, jarFile);
for (JavaClass javaClass : javaClasses.values())
{
System.out.println("Class "+javaClass.getClassName());
Map<JavaClass, Set<Method>> references =
computeReferences(javaClass, javaClasses);
for (Entry<JavaClass, Set<Method>> entry : references.entrySet())
{
JavaClass referencedJavaClass = entry.getKey();
Set<Method> methods = entry.getValue();
System.out.println(
" is referencing class "+
referencedJavaClass.getClassName()+" by calling");
for (Method method : methods)
{
System.out.println(
" "+method.getName()+" with arguments "+
Arrays.toString(method.getArgumentTypes()));
}
}
}
}
private static Map<String, JavaClass> collectJavaClasses(
String jarName, JarFile jarFile)
throws ClassFormatException, IOException
{
Map<String, JavaClass> javaClasses =
new LinkedHashMap<String, JavaClass>();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements())
{
JarEntry entry = entries.nextElement();
if (!entry.getName().endsWith(".class"))
{
continue;
}
ClassParser parser =
new ClassParser(jarName, entry.getName());
JavaClass javaClass = parser.parse();
javaClasses.put(javaClass.getClassName(), javaClass);
}
return javaClasses;
}
public static Map<JavaClass, Set<Method>> computeReferences(
JavaClass javaClass, Map<String, JavaClass> knownJavaClasses)
throws ClassNotFoundException
{
Map<JavaClass, Set<Method>> references =
new LinkedHashMap<JavaClass, Set<Method>>();
ConstantPool cp = javaClass.getConstantPool();
ConstantPoolGen cpg = new ConstantPoolGen(cp);
for (Method m : javaClass.getMethods())
{
String fullClassName = javaClass.getClassName();
String className =
fullClassName.substring(0, fullClassName.length()-6);
MethodGen mg = new MethodGen(m, className, cpg);
InstructionList il = mg.getInstructionList();
if (il == null)
{
continue;
}
InstructionHandle[] ihs = il.getInstructionHandles();
for(int i=0; i < ihs.length; i++)
{
InstructionHandle ih = ihs[i];
Instruction instruction = ih.getInstruction();
if (!(instruction instanceof InvokeInstruction))
{
continue;
}
InvokeInstruction ii = (InvokeInstruction)instruction;
ReferenceType referenceType = ii.getReferenceType(cpg);
if (!(referenceType instanceof ObjectType))
{
continue;
}
ObjectType objectType = (ObjectType)referenceType;
String referencedClassName = objectType.getClassName();
JavaClass referencedJavaClass =
knownJavaClasses.get(referencedClassName);
if (referencedJavaClass == null)
{
continue;
}
String methodName = ii.getMethodName(cpg);
Type[] argumentTypes = ii.getArgumentTypes(cpg);
Method method =
findMethod(referencedJavaClass, methodName, argumentTypes);
Set<Method> methods = references.get(referencedJavaClass);
if (methods == null)
{
methods = new LinkedHashSet<Method>();
references.put(referencedJavaClass, methods);
}
methods.add(method);
}
}
return references;
}
private static Method findMethod(
JavaClass javaClass, String methodName, Type argumentTypes[])
throws ClassNotFoundException
{
for (Method method : javaClass.getMethods())
{
if (method.getName().equals(methodName))
{
if (Arrays.equals(argumentTypes, method.getArgumentTypes()))
{
return method;
}
}
}
for (JavaClass superClass : javaClass.getSuperClasses())
{
Method method = findMethod(superClass, methodName, argumentTypes);
if (method != null)
{
return method;
}
}
return null;
}
}
Обратите внимание, однако, что эта информация может быть неполной во всех смыслах. Например, из-за полиморфизма вы не всегда можете обнаружить, что метод вызывается для объекта определенного класса, потому что он «спрятан» за полиморфной абстракцией. Например, в таком фрагменте кода, как
void call() {
MyClass m = new MyClass();
callToString(m);
}
void callToString(Object object) {
object.toString();
}
вызов toString
фактически происходит в экземпляре MyClass
. Но из-за полиморфизма его можно распознать только как вызов этого метода на "некоторых Object
".
person
Marco13
schedule
05.11.2014