Реализация метода methodNode "visitMethodInsn" в библиотеке 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));
  }

Как видите, байт-код не добавляется в instructions list если версия asm api меньше 5, В чем причина этого?

1 ответ

Решение

Некоторое изучение истории на Gitlab показывает, что рассматриваемый код был добавлен в этот коммит с сообщением коммита Added support for invokespecial and invokestatic on interfaces.

В Java 8 появилась возможность определять неабстрактные методы в интерфейсах, известные как методы по умолчанию. На уровне байт-кода эффект изменения состоит в том, что теперь вы можете использовать методы интерфейса, а также методы класса с invokespecial а также invokestatic инструкции.

До Java 8 при генерации байт-кода вы могли определить тип записи константного пула просто по инструкции: если код операции invokeinterfaceгенерировать InterfaceMethod запись, в противном случае генерировать Method запись. В Java 8 это больше невозможно, потому что invokespecial а также invokestatic являются неоднозначными, что означает, что пользователь должен иметь возможность явно передавать, является ли метод интерфейсным методом или нет. Это означает, что им пришлось добавить дополнительный параметр практически ко всем методам API.

Однако они не хотели нарушать обратную совместимость, что означает, что им нужно сохранить методы со старой сигнатурой (то есть без параметра itf). Эти методы будут направлены на новые, с itf по умолчанию true за invokeinterface инструкции и false в противном случае, что кажется разумным дефолтом. Это то, что делает суперзвук, который вы видите выше. Я не уверен, почему здесь есть переключатель 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));
}

А потом в суперклассе есть

@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);
    }
}

До изменения только методы без itf параметр существовал. Перегруженные версии новые.

Если вы посмотрите внимательно, вы увидите, что эффект от всего делегирования заключается в том, что когда API < 5, он в конечном итоге вызывает старый метод, независимо от того, какой из двух вы вызываете. Если вы вызовете новый метод, он проверит itf параметр перед делегированием. Когда API >= 5, он вызовет новый метод, независимо от того, какой из двух вы вызываете. Если вы вызовете старый метод, он выберет значение по умолчанию для itf перед делегированием.

Так что это не игнорирование вызова метода, это просто делегирование правильной реализации.

Другие вопросы по тегам