1 回答
TA贡献1790条经验 获得超9个赞
仅比较字节码甚至哈希的方法不会产生可靠的解决方案,事实上,对于此类问题根本没有合理的解决方案。
我不知道,其中有多少适用于 Kotlin 编译器,Java 编译器不需要生成相同的字节码,即使使用相同的版本来编译完全相同的源代码。
即使我们假设 Kotlin 编译器具有出色的确定性,甚至跨版本,它也不能忽视 JVM 的演变。例如,任何编译器都不能忽略/指令的删除,即使在尝试保守时也是如此。jsr
ret
但它很可能也会包含其他改进,即使不是被迫的¹。
所以简而言之,即使整个源代码没有改变,假设编译后的形式必须保持不变也是不安全的。即使使用显式确定性编译器,我们也必须在使用更新版本重新编译时为更改做好准备。
更糟糕的是,如果一种方法发生变化,它可能会对其他方法的编译形式产生影响,因为只要需要常量或链接信息,指令就会引用常量池中的项目,并且这些索引可能会发生变化,具体取决于其他方法如何使用常量池。当访问前 255 个池索引之一时,某些指令也有优化的形式,因此编号的更改可能需要更改指令的形式。这反过来可能对其他指令有影响,例如开关指令具有填充字节,具体取决于它们的字节代码位置。
另一方面,如果新常量碰巧在池中与旧常量相同的位置结束,则仅在一个方法中使用的常量值的简单更改可能根本不会影响方法的字节码。
所以,要判断两个方法的代码是否实际上相同,无法绕过指令解析并在一定程度上理解它们的含义。只比较字节或哈希是行不通的。
¹ 命名一些非强制性更改,类文字的编译已更改,同样,字符串连接从 using 更改StringBuffer
为 useStringBuilder
并再次更改为 useStringConcatFactory
,getClass()
内部null
检查的使用更改为requireNonNull(…)
,等等。不同语言的编译器不会必须跟随,但没有人愿意被落在后面……
还有一些错误需要修复,比如过时的指令,没有编译器会为了保持确定性而保留这些错误。
添加回答
举报