降低 类 和方法的可见性

Reduce visibility of classes and methods

TL;DR: 给定字节码,我如何找出给定方法中使用了哪些 classes 和哪些方法?


在我的代码中,我想以编程方式 查找所有 class 具有过多访问限定符的元素和方法。这应该基于对继承、静态用法的分析以及我提供的提示(例如,使用一些像 @KeepPublic 这样的自制注释)来完成。作为一种特殊情况,将找到未使用的 classes 和方法。

我只是做了类似但更简单的事情,即将 final 关键字添加到所有有意义的 class 中(即,它是允许的,而 class 不会被例如 Hibernate 代理)。我以测试的形式做到了这一点,它知道 classes 被忽略(例如,实体)并抱怨所有不必要的非最终 classes.

对于我的所有 classes,我想找到它使用的所有方法和 classes。关于 classes,有 this answer using ASM's Remapper. Concerning methods, I've found an answer proposing instrumentation, which isn't what I want just now. I'm also not looking for a tool like ucdetector 与 Eclipse AST 一起工作。 我如何根据字节码检查方法体?我想自己做,这样我就可以通过编程消除不需要的警告(使用 Lombok 时 ucdetector 有很多警告)。

在每个方法的基础上查看用法,即通过分析所有指令,有一些缺陷。除了 method invocations, there might be method references, which will be encoded using an invokedynamic instruction, having a handle to the target method in its bsm arguments. If the byte code hasn’t been generated from ordinary Java code (or stems from a future version), you have to be prepared to possibly encounter ldc instructions pointing to a handle which would yield a MethodHandle 在运行时。

既然你已经提到了“继承分析”,我只想指出极端情况,即for

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() 调用的目标时,您必须假设有可能,您将找不到太多需要优化的地方。我认为您至少需要内联桥接方法和合成 inner/outer class 访问器来识别继承关系中未使用的成员。

当涉及class 引用时,确实有更多的可能性,使每条指令的分析容易出错。它们可能不仅作为成员访问指令的所有者出现,而且还出现在 newcheckcastinstanceof 和数组特定指令、注释、异常处理程序中,更糟糕的是,在 signatures 可能出现在成员引用、注释、局部变量调试提示等处。ldc 指令可能引用 classes,产生一个 Class 实例,这实际上用于普通 Java 代码,例如对于 class 文字,但如前所述,理论上也有可能产生 MethodHandles,它可能指的是所有者 class,但也有一个带有参数类型的签名和一个 [=56] =] 类型,或生成代表签名的 MethodType

你最好分析常量池,然而,ASM 不提供。准确地说,ClassReader 具有访问池的方法,但它们实际上并不打算由客户端代码使用(如其文档所述)。即使在那里,你也必须意识到陷阱。基本上,一个 CONSTANT_Utf8_info bears a class or signature reference if a CONSTANT_Class_info resp. the descriptor index of a CONSTANT_NameAndType_info or a CONSTANT_MethodType_info points to it. However, declared members of a class have direct references to CONSTANT_Utf8_info pool entries to describe their signatures, see Methods and Fields. Likewise, annotations don’t follow the pattern and have direct references to CONSTANT_Utf8_info entries of the pool assigning a type or signature semantic to it, see enum_const_value and class_info_index

的内容