Generating working invokedynamic instruction with ASM
Asked Answered
S

1

7

I'm working with Java bytecode via ASM and am trying to get a simple invokedynamic example functioning properly. I feel as though I'm fundamentally misunderstanding how invokedynamic is supposed to work. This is what I've tried so far:

In Test2.java I have a static method I wish to invoke and my bootstrap method:

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

Now, in Test.java I generate a class file called Example.class in a package package1 with 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);

However, when I try to run the generated class file, I receive the following:

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)

Replacing the invokedynamic call with the usual invokestatic call (the commented line above) yields the expected result. From the error message it seems to me that the Handle representing my bootstrap method (bootstrap) is not formed properly, but I cannot say for certain. Any help would be much appreciated. I'm using ASM 4.1.

Schoolroom answered 24/9, 2013 at 2:2 Comment(0)
V
7

You're passing in an opcode rather than a handle type as the handle type. Since they're both just ints, there's no type error, but you're providing garbage values which cause a verification error at load time.

You probably meant to do something like this (note the H_ prefix)

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

See here for the Handle documentation.

Villous answered 24/9, 2013 at 5:18 Comment(4)
Yes, this was the issue. Thank you very much.Schoolroom
Thanks again for your answer. I have a small follow up question: I fail to see how invokedynamic is useful if I need to know the types of the parameters to use it, as I do in mv.visitInvokeDynamicInsn("plus", "(II)I", bootstrap, new Object[0]); (in this case it's two ints with return type of int: (II)I). Why would I bother with invokedynamic if I know the parameter/return types? I could just as easily make an invokestatic call, for instance, couldn't I?Schoolroom
@Schoolroom The real magic comes in the bootstrap method and in the variations on CallSite. For example, you can do automatic boxing (example github.com/Storyyeller/Krakatau/blob/master/examples/…), and that's just the tip of the iceberg. You can decide which method to call at runtime, or even change it on the fly during execution. And this is all done more efficiently then using traditional reflection methods.Villous
Another way to put it: for every other kind of invoke bytecode, the types of the parameters passed in match the types that arrive at the target method body. For invokedynamic, you do need to know the types you're passing in, but from that point forward, all manner of transformations and other logic can happen dynamically.Delphine

© 2022 - 2024 — McMap. All rights reserved.