TL; DR:给定字节码,如何找出在给定方法中使用的类和方法?
在我的代码中,我想以编程方式查找具有过于宽泛的访问限定符的所有类和方法。这应该基于对继承,静态用法和我提供的提示的分析来完成(例如,使用一些家庭酿造注释,如@KeepPublic
)。作为特殊情况,将找到未使用的类和方法。
我只是做了类似的事情,虽然简单得多,即将final
关键字添加到有意义的所有类中(即,它是允许的,并且类不会被例如Hibernate代理)。我是以测试的形式完成的,它知道要忽略的类(例如实体),并抱怨所有不必要的非最终类。
对于我的所有类,我想找到它使用的所有方法和类。关于课程,有this answer using ASM's Remapper。关于方法,我发现an answer proposing instrumentation,这不是我现在想要的。我也不是在寻找像{AST}这样适用于Eclipse AST的工具。 如何根据字节码检查方法体?我想自己做,所以我可以通过编程方式消除不需要的警告(使用Lombok时ucdetector很多)。
答案 0 :(得分:3)
以每个方法为基础,即通过分析所有指令来查看使用情况,有一些缺陷。除method invocations之外,可能还有方法引用,它们将使用invokedynamic
instruction进行编码,其bsm
中的目标方法具有handle参数。如果字节代码尚未从普通Java代码生成(或源自未来版本),则必须准备好遇到指向ldc
instructions的handle,这将产生{{3}在运行时。
既然你已经提到过“继承分析”,我只想指出一些极端情况,即
package foo;
class A {
public void method() {}
}
class B implements bar.If {
}
package bar;
public interface If {
void method();
}
很容易忽视A.method()
必须留下public
。
如果您保持保守,即当您无法确定B
个实例是否会最终成为您应用程序中其他位置If.method()
调用的目标时,您必须假设它是可能的,你会发现很多优化。我认为您至少需要内联桥接方法和合成内部/外部类访问器来识别跨继承关系的未使用成员。
当它引入类引用时,确实存在更多的可能性,使每个指令分析容易出错。它们不仅可以作为成员访问说明的所有者,还可以作为new
,checkcast
,instanceof
以及特定于数组的指令,注释,异常处理程序,更糟糕的是,在中签名,可能出现在成员引用,注释,局部变量调试提示等。ldc
指令可能引用类,产生Class
实例,实际上在普通Java代码中使用,例如对于类文字,但如上所述,还有理论上可能生成可能引用所有者类的MethodHandle
,但也有一个签名轴承参数类型和返回类型,或者产生{{3}代表签名。
您最好分析常量池,但ASM不提供。确切地说,ClassReader
具有访问池的方法,但它们实际上并不打算由客户端代码使用(如其文档所述)。即使在那里,你也必须意识到陷阱。基本上,MethodHandle
的内容如果MethodType
,则带有类或签名引用。 CONSTANT_Utf8_info
或CONSTANT_Class_info
的描述符索引指向它。但是,已声明的类成员可以直接引用CONSTANT_Utf8_info
池条目来描述其签名,请参阅CONSTANT_NameAndType_info
和CONSTANT_MethodType_info
。同样,注释不遵循该模式,并直接引用池的CONSTANT_Utf8_info
条目,为其分配类型或签名语义,请参阅Methods和Fields ...