我试图弄清楚如何使用Java pojo,并分析其方法,以便可以调用所有其他方法和函数。例如,这是输出的硬编码示例。我该怎么做这个一般?我需要以编程方式分析Java对象,以确定执行时可以调用的方法。例如:
package com.example.analyze;
public class Main
{
private static class Foo {
public void foo(int value, Bar bar) {
if(value > 5)
bar.gaz();
}
}
private static class Bar {
public void gaz() {
System.out.println("gaz");
}
}
private static class Analyzer {
public void analyze(Object object){
System.out.println("Object method foo could call Bar method gaz");
}
}
public static void main(String[] args)
{
Foo foo = new Foo();
Analyzer analyzer = new Analyzer();
analyzer.analyze(foo);
}
}
答案 0 :(得分:8)
您需要构建一个调用图,然后询问调用图中是否连接了两个节点(调用者和被调用者)。这不是一件容易的事。
您需要做什么:
[您可以使用Wala来避免使用上面的解析/名称类型解析部分,这主要是通过执行上述大部分工作而构建的。
使用调用图,如果您想知道A是否可以调用B,请在调用图中找到A的节点,并查看是否有到B的路径。
这里的另一个注意事项表明这是编译器类的6个月任务。我认为对于经验丰富的编译人员来说,这是6个月,或者更多(我们还没有解决类加载器和反射调用等令人讨厌的问题)。
我认为你最好找到一个解决方案,其他人已经建立了。有人可能;不太可能很容易找到它或她想要分开它。您可能会发现在Univerisities中完成的实现;学术界编写的各种论文(并由原型支持)来计算对象图。缺点是所有那些系统是原型,并由小型,无偿的毕业生团队构建,他们通常不会处理所有边缘情况,更不用说最新版本的Java(lambdas,人?)
答案 1 :(得分:3)
您尝试做的事情被称为static code analysis - 特别是数据流分析,但有一点扭曲......您没有表明您正在查看源代码,但是在编译代码中...如果你想在运行时这样做,你必须处理编译(字节码)代码而不是源代码。因此,您正在寻找能够进行字节码数据流分析的库。有很多图书馆可以提供帮助(现在您知道要搜索什么,如果您愿意,可以找到我推荐的替代方案)。
好的,没有得到一个例子......我喜欢javassist - 我发现它很清楚,因为字节码库可以在线提供很好的示例和文档。 javassit有一些更高级别bytecode analysis API,所以你甚至可能不需要深入挖掘,这取决于你需要做什么。
要打印上述Foo / Bar示例的输出,请使用以下代码:
public static void main (String... args) throws Exception {
Analyzer a = new Analyzer();
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Foo");
for (CtMethod cm : cc.getDeclaredMethods()) {
Frame[] frames = a.analyze(cm);
for (Frame f : frames) {
System.out.println(f);
}
}
}
将打印:
locals = [test.Foo, int, test.Bar] stack = []
locals = [test.Foo, int, test.Bar] stack = [int]
locals = [test.Foo, int, test.Bar] stack = [int, int]
null
null
locals = [test.Foo, int, test.Bar] stack = []
locals = [test.Foo, int, test.Bar] stack = [test.Bar]
null
null
locals = [test.Foo, int, test.Bar] stack = []
如果您需要更多详细信息,则需要实际读取字节码,并使用JVM specification方便:
public static void main (String... args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("test.Foo");
for (CtMethod cm : cc.getDeclaredMethods()) {
MethodInfo mi = cm.getMethodInfo();
CodeAttribute ca = mi.getCodeAttribute();
CodeIterator ci = ca.iterator();
while (ci.hasNext()) {
int index = ci.next();
int op = ci.byteAt(index);
switch (op) {
case Opcode.INVOKEVIRTUAL:
System.out.println("virutal");
//lookup in the JVM spec how to extract the actual method
//call info here
break;
}
}
}
}
我希望这有助于你开始=)
答案 2 :(得分:3)
您可以使用ASM api查找有关类文件的信息,示例代码可以很好地了解如何获取方法详细信息。
分析器类
package sample.code.analyze;
import java.io.IOException;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class Analyzer {
public void analyze(Object object) {
ClassVisitor cv = new ClassVisitor(Opcodes.ASM4) {
@Override
public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
System.out.println("Method: " + name + " -- " + desc);
return new MethodVisitor(Opcodes.ASM4) {
@Override
public void visitMethodInsn(int opcode, String owner,
String name, String desc, boolean arg4) {
System.out.println("-- opcode -- " + opcode
+ " -- owner -- " + owner + "name -- "
+ name + "desc -- " + desc);
super.visitMethodInsn(opcode, owner, name, desc, arg4);
}
};
}
};
try {
ClassReader classReader = new ClassReader(object.getClass().getCanonicalName());
classReader.accept(cv, 0);
} catch (IOException e) {
System.err.println("Something went wrong !! " + e.getMessage());
}
}
public static void main(String[] args) {
Foo foo = new Foo();
Analyzer analyzer = new Analyzer();
analyzer.analyze(foo);
}
}
酒吧类
package sample.code.analyze;
public class Bar {
public void gaz() {
System.out.println("gaz");
}
}
Foo Class
package sample.code.analyze;
import sample.code.analyze.Bar;
public class Foo {
public void foo(int value, Bar bar) {
if (value > 5) {
bar.gaz();
}
}
}
答案 3 :(得分:1)
这非常困难 - 您需要使用Java Reflect API并进行一些繁重的解析以及编译器可以完成的大量工作。相反,您可以使用已有的许多Java依赖工具/插件之一(如https://stackoverflow.com/a/2366872/986160中的JDepend)
答案 4 :(得分:1)
OP答案供参考:
目标是让它发挥作用:
MethodInvocationGraph methodInvocationGraph =
new MethodInvocationGraph(
Disassembler.disassembleThisJar());
methodInvocationGraph.printObjectMethodDependencyTree(methodInvocationGraph);
这将打印对象自己的依赖项。要做到这一点,你需要:
深入了解ASM树API:
打开和访问Jar内容的方法,包括
MethodInvocationGraph.class.getProtectionDomain().getCodeSource()
JNI签名解析器
http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/method.html
和
等图表框架答案 5 :(得分:-1)
由于静态类型的原因,方法存在问题。 静态方法将在类的开始时间调用第一个执行因此,所有将在第一阶段执行,并且由于方法的静态质量而无法进行第二次调用。所以 main方法无法调用上面的方法。
答案 6 :(得分:-1)
如果您调用任何方法,我认为您可以从stacktrace获取所有信息。当我们得到任何异常时,我们可以使用printStackTrace()看到堆栈跟踪;方法。这不是一个答案,但它可以帮助您找到解决问题的方法。