降低类和方法的可见性

时间:2017-05-01 21:54:15

标签: java bytecode access-modifiers unreachable-code

TL; DR:给定字节码,如何找出在给定方法中使用的类和方法?

在我的代码中,我想以编程方式查找具有过于宽泛的访问限定符的所有类和方法。这应该基于对继承,静态用法和我提供的提示的分析来完成(例如,使用一些家庭酿造注释,如@KeepPublic)。作为特殊情况,将找到未使用的类和方法。

我只是做了类似的事情,虽然简单得多,即将final关键字添加到有意义的所有类中(即,它是允许的,并且类不会被例如Hibernate代理)。我是以测试的形式完成的,它知道要忽略的类(例如实体),并抱怨所有不必要的非最终类。

对于我的所有类,我想找到它使用的所有方法和类。关于课程,有this answer using ASM's Remapper。关于方法,我发现an answer proposing instrumentation,这不是我现在想要的。我也不是在寻找像{AST}这样适用于Eclipse AST的工具。 如何根据字节码检查方法体?我想自己做,所以我可以通过编程方式消除不需要的警告(使用Lombok时ucdetector很多)。

1 个答案:

答案 0 :(得分:3)

以每个方法为基础,即通过分析所有指令来查看使用情况,有一些缺陷。除method invocations之外,可能还有方法引用,它们将使用invokedynamic instruction进行编码,其bsm中的目标方法具有handle参数。如果字节代码尚未从普通Java代码生成(或源自未来版本),则必须准备好遇到指向ldc instructionshandle,这将产生{{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()调用的目标时,您必须假设它是可能的,你会发现很多优化。我认为您至少需要内联桥接方法和合成内部/外部类访问器来识别跨继承关系的未使用成员。

当它引入类引用时,确实存在更多的可能性,使每个指令分析容易出错。它们不仅可以作为成员访问说明的所有者,还可以作为newcheckcastinstanceof以及特定于数组的指令,注释,异常处理程序,更糟糕的是,在中签名,可能出现在成员引用,注释,局部变量调试提示等。ldc指令可能引用类,产生Class实例,实际上在普通Java代码中使用,例如对于类文字,但如上所述,还有理论上可能生成可能引用所有者类的MethodHandle,但也有一个签名轴承参数类型和返回类型,或者产生{{3}代表签名。

您最好分析常量池,但ASM不提供。确切地说,ClassReader具有访问池的方法,但它们实际上并不打算由客户端代码使用(如其文档所述)。即使在那里,你也必须意识到陷阱。基本上,MethodHandle的内容如果MethodType,则带有类或签名引用。 CONSTANT_Utf8_infoCONSTANT_Class_info的描述符索引指向它。但是,已声明的类成员可以直接引用CONSTANT_Utf8_info池条目来描述其签名,请参阅CONSTANT_NameAndType_infoCONSTANT_MethodType_info。同样,注释不遵循该模式,并直接引用池的CONSTANT_Utf8_info条目,为其分配类型或签名语义,请参阅MethodsFields ...