ASM库中MethodNode的'visitMethodInsn'方法的实现

时间:2018-06-24 09:20:15

标签: java bytecode java-bytecode-asm

这是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));
  }

如您所见,如果asm api版本小于instructions list,则不会将字节码添加到5中。背后的原因是什么?

1 个答案:

答案 0 :(得分:1)

在Gitlab上的一些历史调查显示,有问题的代码已添加到this commit中,并带有提交消息Added support for invokespecial and invokestatic on interfaces.

Java 8引入了在接口中定义非抽象方法的功能,称为默认方法。在字节码级别,更改的结果是您现在可以使用带有invokespecialinvokestatic指令的接口方法以及类方法。

在Java 8之前,生成字节码时,您可以仅通过以下指令确定常量池条目的类型:如果操作码为invokeinterface,则生成InterfaceMethod条目,否则生成{ {1}}个条目。在Java 8中,这不再可能,因为Methodinvokespecial含糊不清,这意味着用户需要能够显式传递该方法是否为接口方法。这意味着他们必须在几乎所有方法api上添加一个额外的参数。

但是,他们不想破坏向后兼容性,这意味着他们需要使用旧签名(即没有itf参数)保留方法。这些方法将转发给新方法,其中invokestatic的{​​{1}}指令默认为itf,否则为true,这似乎是一个合理的默认值。这就是您在上面看到的超级调用的作用。我不确定为什么要使用API​​ <5开关,但是我怀疑这是为了确保向后兼容性,还是要打破他们方法分配方案中的无限循环。

在旁注中,invokeinterface在大约8个月前被作为重大代码重组的一部分而删除,因此您不会在ASM的最新版本中看到它。

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

作为参考,以下是相关代码: false

MethodNode

然后在超类中,就有

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)); } 参数的方法。重载的版本是新的。

如果仔细观察,您会发现所有委托的结果是,当API <5时,无论您调用的是哪种方法,它都会最终调用旧方法。如果调用新方法,它将在委派之前验证@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); } } 参数。当API> = 5时,无论您调用的是哪种方法,它都会最终调用新方法。如果您调用旧方法,则它将在委派之前为itf选择默认值。

因此,它不会忽略方法调用,而只是委托给正确的实现。