Реализация метода methodNode «visitMethodInsn» в библиотеке ASM - PullRequest
0 голосов
/ 24 июня 2018

Это тело 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 Ответ

0 голосов
/ 24 июня 2018

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

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

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

Однако они не хотели нарушать обратную совместимость, а это значит, что им нужно сохранять методы со старой подписью (т. Е. Без параметра itf). Эти методы переадресовывают на новые, с itf по умолчанию true для invokeinterface инструкций и false в противном случае, что кажется разумным значением по умолчанию. Это то, что делает суперзвезда, которую вы видите выше. Я не уверен, почему здесь есть переключатель API <5, но я подозреваю, что это либо для обеспечения обратной совместимости, либо для прерывания бесконечного цикла в их схеме диспетчеризации методов. </p>

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

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

...