向后兼容

介绍

过去已经以类文件格式引入了新元素,并且将来将继续添加新元素(例如,用于模块化,Java类型的注释等)。

直到ASM 3.x,每个此类更改都导致ASM API中向后不兼容的更改,这是不好的。

为了解决这些问题,ASM 4.0中引入了一种新的机制。

其目标是确保即使将来在类文件格式中引入了新功能时,所有将来的ASM版本都将保持与任何先前版本(直至ASM 4.0)的向后兼容性。

这意味着从4.0开始,为一个ASM版本编写的类生成器,类分析器或类适配器仍可用于将来的任何ASM版本。

但是,仅由ASM不能确保此属性。

它要求用户在编写代码时遵循一些简单的准则。

本章的目的是介绍这些准则,并给出ASM核心API中使用的内部机制的概念,以确保向后兼容。

注意

ASM 4.0中引入的向后兼容机制需要将ClassVisitor,FieldVisitor,MethodVisitor等从接口更改为抽象类,并使用构造函数以ASM版本作为参数。

如果您的代码是针对ASM 3.x实施的,则可以通过在代码分析器和适配器中将扩展替换为工具,并在其构造函数中指定ASM版本,将其升级到ASM 4.0。

此外,ClassAdapter和MethodAdapter已合并为ClassVisitor和MethodVisitor。

要转换代码,只需要将ClassAdapter替换为ClassVisitor,将MethodAdapter替换为MethodVisitor。

另外,如果定义了自定义FieldAdapter或AnnotationAdapter类,则现在可以将它们替换为FieldVisitor和AnnotationVisitor。

向后兼容合约

在介绍用户指南以确保向后兼容之前,我们在此更精确地定义“向后兼容”的含义。

首先,研究新的类文件功能如何影响代码生成器,分析器和适配器非常重要。

也就是说,与任何实现和二进制兼容性问题无关,在引入这些新功能之前设计的类生成器,分析器或适配器在这些修改之后是否仍然有效?

换句话说,如果我们假设新功能被简单地忽略并通过转换链原封不动地传递,在引入之前已签名,此链条仍然有效吗?

实际上,对于类生成器,分析器和适配器,影响是不同的:

  • 类生成器

类生成器不受影响:它们生成具有某些固定类版本的代码,并且这些生成的类将在将来的JVM版本中保持有效,因为JVM确保了向后二进制兼容性。

  • 类分析器

类分析器可能会受到影响,也可能不会受到影响。

例如,尽管引入了注解,但是分析针对Java 4编写的字节码指令的代码仍可能适用于Java 5类。

但是,这些相同的代码可能不再适用于Java 7类,因为它不能忽略新的 invokedynamic 指令。

  • 类适配器

类适配器可能会受到影响,也可能不会受到影响。

无效代码删除工具不受注释的引入甚至新的invokedynamic指令的影响。

另一方面,类重命名工具受这两者的影响。

说明

这表明新的类文件功能可能会对现有的类分析器或适配器产生不可预测的影响。

如果只是简单地忽略了新功能并通过分析或转换链将其不变地传递,则有时该链将无错误运行并产生有效结果,有时它将运行时没有错误,但会产生无效的结果,有时在执行过程中会失败。

第二种情况尤其成问题,因为它在用户不知道的情况下破坏了分析或转换链的语义。

这可能导致难以发现错误。

为了解决这个问题,我们认为最好在分析或转换链中遇到未知特征后立即提出错误,而不是忽略新特征

该错误表明该链可能无法使用新的类格式,并且它的作者必须分析情况以在必要时进行更新。

协议

所有这些导致对以下向后兼容协议的定义:

  1. ASM版本X是为版本小于或等于x的Java类编写的。它不能生成版本 y > x 的类,并且在ClassReader.accept中输入大于x的类作为输入时必须失败。

  2. 为ASM X编写并遵循下面介绍的准则的代码必须能够继续工作,并且未经修改,并且输入类的版本最高为x,并且ASM的将来版本为 Y>X

  3. 为ASM X编写并遵循下面介绍的准则的代码必须继续使用未经声明的输入类,这些类的声明版本为y,但仅使用具有ASM Y或更高版本的旧版本或等于x的版本中定义的功能。

  4. 如果为输入提供了使用在类版本 y > x 中引入的功能以及ASM X或任何其他将来版本的类,则为ASM X编写并遵循以下准则的代码必须失败。

请注意,最后三点与类别生成器无关,后者没有类别输入。

一个例子

为了说明用户指南和确保向后兼容性的内部ASM机制,我们在本章中假设将向Java 8类添加两个新的虚构属性,一个用于存储类作者,一个用于存储其许可证。

我们还假设这些新属性将通过ASM 5.0中的ClassVisitor中的两个新方法公开:

void visitLicense(String license);

来访问许可证,并使用新版本的visitSource来与源文件名和调试信息同时访问作者

void visitSource(String author, String source, String debug);

旧的visitSource方法仍然有效,但是在ASM 5.0中声明为不推荐使用:

@Deprecated 
void visitSource(String source, String debug);

author和license属性是可选的,即,调用visitLicense不是必需的,在visitSource调用中author可以为null。

参考文档

https://asm.ow2.io/asm4-guide.pdf