Java 实施';访问方法n';ASM库中MethodNode的方法

Java 实施';访问方法n';ASM库中MethodNode的方法,java,bytecode,java-bytecode-asm,Java,Bytecode,Java Bytecode Asm,这是visitMethodInsnMethodNode类的方法的主体: @Override public void visitMethodInsn( final int opcode, final @InternalForm String owner, final @Identifier String name, final @MethodDescriptor String descriptor, final boolean i

这是
visitMethodInsn
MethodNode类的方法的主体:

  @Override
  public void visitMethodInsn(
      final int opcode,
      final @InternalForm String owner,
      final @Identifier String name,
      final @MethodDescriptor String descriptor,
      final boolean isInterface) {
    if (api < Opcodes.ASM5) {
      super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
      return;
    }
    instructions.add(new MethodInsnNode(opcode, owner, name, descriptor, isInterface));
  }
@覆盖
公共无效访问方法n(
最终int操作码,
最终@InternalForm字符串所有者,
最终@Identifier字符串名称,
最终@MethodDescriptor字符串描述符,
最终布尔值(isInterface){
如果(api

如您所见,如果asm api版本小于
5
,字节码不会添加到
说明列表中。这背后的原因是什么?

对Gitlab历史记录的一些调查显示,所讨论的代码是添加进来的,提交消息
增加了对接口上的InvokSpecial和invokestatic的支持。

Java8引入了在接口中定义非抽象方法的能力,称为默认方法。在字节码级别,更改的效果是您现在可以在
invokespecial
invokestatic
指令中使用接口方法和类方法

在Java 8之前的版本中,在生成字节码时,您可以通过以下指令简单地确定常量池项的类型:如果操作码是
invokeinterface
,则生成
InterfaceMethod
项,否则生成
方法
项。在Java8中,这不再可能,因为
invokespecial
invokestatic
是不明确的,这意味着用户需要能够显式地传递方法是否是接口方法。这意味着他们必须在几乎所有的方法API中添加一个额外的参数

但是,他们不想破坏向后兼容性,这意味着他们需要保留具有旧签名的方法(即没有itf参数)。这些方法将转发到新的方法,对于
invokeinterface
指令,
itf
默认为
true
,否则为
false
,这似乎是一个合理的默认值。这就是你在上面看到的supercall所做的。我不确定为什么会有API<5开关,但我怀疑它要么是为了确保向后兼容性,要么是为了打破方法调度方案中的无限循环

另一方面,
MethodNode
大约在8个月前被删除,作为主要代码重组的一部分,因此在最新版本的ASM中不会看到它

编辑:我看到您对方法委派感到困惑。这是相当棘手的,因为涉及到四种不同的方法

以下是所涉及的代码,供参考:
MethodNode

@Deprecated
@Override
public void visitMethodInsn(int opcode, String owner, String name,
        String desc) {
    if (api >= Opcodes.ASM5) {
        super.visitMethodInsn(opcode, owner, name, desc);
        return;
    }
    instructions.add(new MethodInsnNode(opcode, owner, name, desc));
}

@Override
public void visitMethodInsn(int opcode, String owner, String name,
        String desc, boolean itf) {
    if (api < Opcodes.ASM5) {
        super.visitMethodInsn(opcode, owner, name, desc, itf);
        return;
    }
    instructions.add(new MethodInsnNode(opcode, owner, name, desc, itf));
}
@已弃用
@凌驾
public void visitMethodInsn(int操作码、字符串所有者、字符串名称、,
字符串描述){
如果(api>=操作码.ASM5){
超级访问方法(操作码、所有者、名称、描述);
回来
}
添加(新的MethodInsnNode(操作码、所有者、名称、描述));
}
@凌驾
public void visitMethodInsn(int操作码、字符串所有者、字符串名称、,
字符串描述,布尔值(itf){
如果(api
然后在超类中,有

@Deprecated
public void visitMethodInsn(int opcode, String owner, String name,
        String desc) {
    if (api >= Opcodes.ASM5) {
        boolean itf = opcode == Opcodes.INVOKEINTERFACE;
        visitMethodInsn(opcode, owner, name, desc, itf);
        return;
    }
    if (mv != null) {
        mv.visitMethodInsn(opcode, owner, name, desc);
    }
}

public void visitMethodInsn(int opcode, String owner, String name,
        String desc, boolean itf) {
    if (api < Opcodes.ASM5) {
        if (itf != (opcode == Opcodes.INVOKEINTERFACE)) {
            throw new IllegalArgumentException(
                    "INVOKESPECIAL/STATIC on interfaces require ASM 5");
        }
        visitMethodInsn(opcode, owner, name, desc);
        return;
    }
    if (mv != null) {
        mv.visitMethodInsn(opcode, owner, name, desc, itf);
    }
}
@已弃用
public void visitMethodInsn(int操作码、字符串所有者、字符串名称、,
字符串描述){
如果(api>=操作码.ASM5){
布尔itf=操作码==操作码.INVOKEINTERFACE;
访问方法(操作码、所有者、名称、描述、itf);
回来
}
如果(mv!=null){
mv.visitMethodInsn(操作码、所有者、名称、描述);
}
}
public void visitMethodInsn(int操作码、字符串所有者、字符串名称、,
字符串描述,布尔值(itf){
如果(api
在更改之前,只有不带
itf
参数的方法存在。重载版本是新的

如果仔细观察,您可以看到所有委托的效果是,当API<5时,它将最终调用旧方法,而不管您调用两个方法中的哪一个。如果调用新方法,它将在委派之前验证
itf
参数。当API>=5时,它将最终调用新方法,而不管您调用这两个方法中的哪一个。如果调用旧方法,它将在委派之前为
itf
选择一个默认值


因此,它并没有忽略方法调用,它只是简单地委托给正确的实现。

我的查询是关于
API<5
开关的。这似乎表明,如果条件为
true
,则将完全忽略
visitMethodInsn
事件。这背后的原因是什么?@saga我编辑了我的答案来解释方法授权的内容。它不是忽略调用,只是将其委托给方法的旧版本。