java - 跟踪类字节码中方法实现的变化

标签 java bytecode java-bytecode-asm

我在一些 kotlin 代码中有一些抽象项目(让我们称之为项目)字节码(它是每个类的),每个类字节码都存储为 ByteArray;任务是告诉每个类中的哪些特定方法在项目的构建之间被修改。也就是说,同一个类的The Project有两个ByteArray,但是属于不同的版本,需要比较准确。一个简单的例子。假设我们有一个简单的类:

class Rst {

    fun getjson(): String {
        abc("""ss""");
        return "jsonValid"
    }

    public fun abc(s: String) {
        println(s)
    }

}

它的字节码存储在oldByteCode中。现在类发生了一些变化:

class Rst {

        fun getjson(): String {
            abc("""ss""");
            return "someOtherValue"
        }

        public fun newMethod(s: String) {
            println("it's not abc anymore!")
        }

    }

它的字节码存储在 newByteCode 中。 这是主要目标:将 oldByteCode 与 newByteCode 进行比较。

这里我们有以下改动:

  • getjson() 方法已更改;
  • abc() 方法已被删除;
  • newMethod() 已创建。

因此,如果方法的签名保持不变,则该方法已更改。如果不是,它已经是一些不同的方法。

现在回到实际问题。我必须通过字节码了解每个方法的确切状态。我现在拥有的是 jacoco 分析器,它将类字节码解析为“包”。在这些包中,我有包、类、方法的层次结构,但只有它们的签名,所以我无法判断方法的主体是否有任何变化。我只能跟踪签名差异。 是否有任何工具、库可以将类字节码拆分为其方法字节码?例如,我可以用它们计算哈希值并进行比较。也许 asm 库对此有任何处理? 欢迎任何想法。

最佳答案

TL;DR 你的方法只是比较字节码甚至哈希不会产生可靠的解决方案,事实上,对于这类问题根本没有任何解决方案。

我不知道,其中有多少适用于 Kotlin 编译器,但正如 Is the creation of Java class files deterministic? 中所阐述的那样,Java 编译器不需要生成相同的字节码,即使使用相同的版本来编译完全相同的源代码。虽然他们可能有一个试图尽可能确定的实现,但在查看不同版本或替代实现时情况会发生变化,如 Do different Java Compilers (where the vendor is different) produce different bytecode 中所述。 .

即使我们假设 Kotlin 编译器具有出色的确定性,即使跨版本,它也不能忽视 JVM 的演变。例如。 the removal of the jsr/ret instructions任何编译器都不能忽略,即使试图保守。但它很可能也会包含其他改进,即使不是被迫的¹。

所以简而言之,即使整个源代码没有改变,假设编译后的形式必须保持不变也是不安全的。即使使用显式确定性编译器,我们也必须在使用较新版本重新编译时为更改做好准备。

更糟糕的是,如果一个方法发生变化,它可能会对其他方法的编译形式产生影响,因为只要需要常量或链接信息,指令就会引用常量池中的项目,并且这些索引可能会发生变化,具体取决于另一个方法方法使用常量池。在访问前 255 个池索引之一时,某些指令也有优化的形式,因此编号的更改可能需要更改指令的形式。这反过来可能会对其他指令产生影响,例如开关指令具有填充字节,具体取决于它们的字节码位置。

另一方面,仅在一个方法中使用的常量值的简单更改可能根本不会影响方法的字节码,如果新常量恰好与旧常量在池中的相同位置结束.

因此,要判断两个方法的代码是否实际上相同,无法在一定程度上解析指令并理解其含义。仅比较字节或哈希是行不通的。

¹ 列举一些非强制性更改,the compilation of class literals更改了,同样,字符串连接从使用 StringBuffer 更改为使用 StringBuilderchanged again to use StringConcatFactory , 使用getClass() for intrinsic null checks changed to requireNonNull(…)等。不必遵循针对不同语言的编译器,但没有人愿意落在后面......

还有 bug 需要修复,比如 obsolete instructions ,没有编译器会为了保持确定性而保留它。

关于java - 跟踪类字节码中方法实现的变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57161536/

相关文章:

java - Tomcat context.xml 和 context.properties 文件

java - Java 中的动态链接 : Verifier vs Resolution

java - ASM 4(Java 库)教程?

java - JBoss 类中的 LocalVariableTable 已损坏

java - 用于方法入口/导出的 ASM 字节码检测

java - 字符串生成器和字符串输出

java - 当我们声明静态时分配的内存在哪里?

java - 为什么 HashSet 没有稳定的序列化?

java - 有什么方法可以知道执行字节代码时 JVM 的速度吗?

java - 是否可以从 java.* 包中检测类