介绍
与核心API一样,ASM 4.0的树API中也引入了一种新机制,以确保在将来的ASM版本中向后兼容。
但是,这里再次不能单独通过ASM来确保此属性。
要求用户在编写自己的文章时遵循一些简单的准则 码。
本章的目的是介绍这些准则,并给出ASM树API中使用的内部机制的概念,以确保向后兼容。
指导方针
本节介绍了使用ASM树API时必须遵循的准则,以确保您的代码在以后的任何ASM版本中保持有效(按照5.1.1节中定义的约定)。
首先,如果您使用树API编写类生成器,则没有遵循的指南(与核心API一样)。
您可以使用任何构造函数创建ClassNode和其他元素,并使用这些类的任何方法。
另一方面,如果您使用树API编写类分析器或类适配器,即,如果您使用ClassNode或通过 ClassReader.accept()
直接或间接填充的其他类似类,或者重写了这些类,那么您必须遵循以下几条准则。
Basic rules
创建类节点
我们在这里考虑以下情况:
创建一个ClassNode,通过ClassReader填充它,然后分析或转换它,然后再选择使用ClassWriter编写结果(其他节点类的讨论和准则是相同的;分析或转换ClassNode 由其他人创建的内容将在下一部分中讨论)。
在这种情况下,只有一个准则:
准则3:
要使用ASM版本X的树API编写类分析器或适配器,请使用具有该确切版本作为参数的构造函数(而不是没有参数的默认构造函数)来创建ClassNode。
本指南的目标是在通过ClassReader(在向后兼容性合同中定义)填充ClassNode时,遇到未知功能时立即引发错误。
如果不遵循它,则您的分析或转换代码可能会在以后遇到未知元素时失败,或者可能会成功,但会产生错误结果,因为它不应该忽略这些未知元素。
换句话说,如果不遵守该准则,则可能无法确保合同的最后条款。
这是如何运作的? 在内部,ClassNode在ASM 4.0中实现如下(我们在此重用5.1.2节的示例):
public class ClassNode extends ClassVisitor {
public ClassNode() {
super(ASM4, null);
}
public ClassNode(int api) {
super(api, null);
}
...
public void visitSource(String source, String debug) {
// store source and debug in local fields ...
}
}
在ASM 5.0中,此代码变为:
public class ClassNode extends ClassVisitor {
...
public void visitSource(String source, String debug) {
if (api < ASM5) {
// store source and debug in local fields ...
} else {
visitSource(null, source, debug);
}
}
public void visitSource(Sring author, String source, String debug) {
if (api < ASM5) {
if (author == null)
visitSource(source, debug);
else
throw new RuntimeException();
} else {
// store author, source and debug in local fields ...
}
}
public void visitLicense(String license) {
if (api < ASM5) throw new RuntimeException();
// store license in local fields ...
}
}
如果使用ASM 4.0,则创建ClassNode(ASM4)并没有什么特别的。
但是,如果升级到ASM 5.0,而无需更改代码,则将获得ClassNode 5.0,其api字段将为 ASM4 < ASM5
。
然后很容易看到,如果输入类包含非null的author或license属性,则按照我们的合同中的定义,通过ClassReader填充ClassNode将失败。
如果还升级代码,将api字段更改为ASM5并更新其余代码以考虑这些新属性,则在填充节点时不会引发任何错误。
请注意,ClassNode 5.0代码与ClassVisitor 5.0代码非常相似。
如果定义ClassNode的子类(类似于ClassVisitor的子类-参见10.2.2节),则可以确保语义正确。
使用现有的类节点
如果您的类分析器或适配器收到了其他人创建的ClassNode,则不能确定创建时已传递给其构造函数的ASM版本。
您可以自己检查api字段,但是如果您发现此版本高于您支持的版本,则仅拒绝该类将太保守。
确实,此类可能不包含任何未知功能。
另一方面,您无法测试是否存在未知功能(在我们的示例场景中,由于您不知道,在编写用于ASM 4.0的代码时,如何测试您的ClassNode中不存在未知许可证字段? 在现阶段是否会在将来添加这样的字段?)。
ClassNode.check()
方法旨在解决此问题。
这导致了以下准则:
准则4:
准则4:使用其他人创建的ClassNode,使用ASM版本X的树API编写类分析器或适配器,在以任何方式使用ClassNode之前,以其确切版本作为参数调用其 check() 方法。
目标与准则3相同:如果不遵循该准则,则可能无法确保合同的最后条款。
在内部,检查方法是在ASM 4.0中实现的
public class ClassNode extends ClassVisitor {
...
public void check(int api) {
// nothing to do
}
}
In ASM 5.0 this code becomes:
public class ClassNode extends ClassVisitor {
...
public void check(int api) {
if (api < ASM5 && (author != null || license != null)) {
throw new RuntimeException();
}
}
}
如果您的代码是针对ASM 4.0编写的,并且如果您获得的ClassNode 4.0(其api字段为ASM4),则不会出现问题,并且检查不执行任何操作。
但是,如果您获得ClassNode 5.0,则如果该节点实际上包含非空作者或许可证,即如果它包含ASM 4.0中未知的新功能,则check(ASM4)方法将失败。
注意
如果您自己创建ClassNode,也可以使用此准则。
然后,您无需遵循准则3,即无需在ClassNode构造函数中指定ASM版本。
这些检查将改为在check方法中进行(但是,这可能会比填充ClassNode时进行更早的检查效率低)。
继承规则
如果要提供ClassNode的子类或其他类似的节点类,则适用准则1和2。
请注意,在特殊情况下(通常使用)重写了 visitEnd()
方法的MethodNode匿名子类:
class MyClassVisitor extends ClassVisitor {
...
public MethodVisitor visitMethod(...) {
final MethodVisitor mv = super.visitMethod(...);
if (mv != null) {
return new MethodNode(ASM4) {
public void visitEnd() {
// perform a transformation
accept(mv);
}
}
}
return mv;
}
}
则准则2将自动执行(尽管匿名类未明确声明为final,但不能重写该匿名类)。
您只需要遵循准则3,即在MethodNode构造函数中指定ASM版本(或遵循准则4,即在执行转换之前调用check(ASM4))。
其他包
每个构造函数的 asm.util 和 asm.commons 中的类都有两个变体:
一个带有ASM版本参数,一个带有ASM版本参数。
如果您只想实例化和使用asm.util中的ASMifier,Textifier或CheckXxxAdapter类,或者asm.commons包中的任何类,则可以使用不带ASM版本的构造函数实例化它们 参数。
您也可以使用带有ASM版本参数的构造函数,但这不必要地将这些组件限制为指定的ASM版本(使用no-arg构造函数等效于说“使用最新的ASM版本”)。
这就是为什么将使用ASM版本参数的构造函数声明为受保护的原因。
另一方面,如果您要覆盖ASMifier,TextifierVisitor, 或asm.util中的CheckXxxAdapter类,或asm.commons包中的任何类,则适用准则1和2。
特别是,您的构造函数必须使用要用作参数的ASM版本调用super(…)。
最后,如果要使用vs.覆盖asm.tree.analysis中的Interpreter类或其子类,则必须进行相同的区分。
还要注意,在使用分析包之前,您必须创建一个MethodNode或从其他人那里获得一个MethodNode,并且在将此节点传递到Analyzer之前,必须在此处使用准则3和4。
个人感受
代码之间的兼容性是一件非常重要的事情。