Генерация рабочей инструкции invokedynamic с помощью ASM

Я работаю с байт-кодом Java через ASM и пытаюсь заставить простой invokedynamic пример работать правильно. Мне кажется, что я в корне неправильно понимаю, как должен работать invokedynamic. Это то, что я пробовал до сих пор:

В Test2.java у меня есть статический метод, который я хочу вызвать, и мой метод начальной загрузки:

public static int plus(int a, int b) {  // method I want to dynamically invoke
    return a + b;
}

public static CallSite bootstrap(MethodHandles.Lookup caller, String name,
        MethodType type) throws Exception {

    MethodHandle mh = MethodHandles.lookup().findStatic(Test2.class,
            "plus", MethodType.methodType(int.class, int.class, int.class));

    return new ConstantCallSite(mh);
}

Теперь в Test.java я создаю файл класса с именем Example.class в пакете package1 с помощью ASM:

mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
        "([Ljava/lang/String;)V", null, null);
mv.visitCode();

mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
        "Ljava/io/PrintStream;");

mv.visitIntInsn(BIPUSH, 42);
mv.visitIntInsn(BIPUSH, 24);

// mv.visitMethodInsn(INVOKESTATIC, "package1/Test2", "plus", "(II)I");

MethodType mt = MethodType.methodType(CallSite.class,
        MethodHandles.Lookup.class, String.class, MethodType.class);

Handle bootstrap = new Handle(Opcodes.INVOKESTATIC, "package1/Test2",
        "bootstrap", mt.toMethodDescriptorString());

mv.visitInvokeDynamicInsn("plus", "(II)I", bootstrap, new Object[0]);

mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
        "(I)V");

mv.visitInsn(RETURN);

Однако, когда я пытаюсь запустить сгенерированный файл класса, я получаю следующее:

Exception in thread "main" java.lang.ClassFormatError: Bad method handle kind at constant pool index 23 in class file package1/Example
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:792)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

Замена вызова invokedynamic на обычный вызов invokestatic (закомментированная строка выше) дает ожидаемый результат. Из сообщения об ошибке мне кажется, что Handle, представляющий мой метод начальной загрузки (bootstrap), сформирован неправильно, но я не могу сказать наверняка. Любая помощь приветствуется. Я использую ASM 4.1.


person arshajii    schedule 24.09.2013    source источник


Ответы (1)


Вы передаете код операции, а не тип дескриптора в качестве типа дескриптора. Поскольку они оба просто целые, ошибки типа нет, но вы предоставляете значения мусора, которые вызывают ошибку проверки во время загрузки.

Вероятно, вы хотели сделать что-то подобное (обратите внимание на префикс H_)

Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, "package1/Test2",
        "bootstrap", mt.toMethodDescriptorString());

См. здесь документацию по дескриптору.

person Antimony    schedule 24.09.2013
comment
Да, это было проблемой. Спасибо большое. - person arshajii; 24.09.2013
comment
Еще раз спасибо за ваш ответ. У меня есть небольшой дополнительный вопрос: я не понимаю, насколько полезен invokedynamic, если мне нужно знать типы параметров для его использования, как я делаю в mv.visitInvokeDynamicInsn("plus", "(II)I", bootstrap, new Object[0]); (в данном случае это два int с возвращаемым типом int: (II)I) . Зачем мне заморачиваться с invokedynamic, если я знаю типы параметров/возвратов? Я мог бы так же легко сделать вызов invokestatic, не так ли? - person arshajii; 25.09.2013
comment
@arshajii Настоящая магия заключается в методе начальной загрузки и в вариантах CallSite. Например, вы можете сделать автоматический бокс (пример github.com/Storyyeller/ Krakatau/blob/master/examples/), и это только верхушка айсберга. Вы можете решить, какой метод вызывать во время выполнения, или даже изменить его на лету во время выполнения. И все это делается более эффективно, чем при использовании традиционных методов отражения. - person Antimony; 25.09.2013