Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating working invokedynamic instruction with ASM

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.

like image 719
arshajii Avatar asked Sep 24 '13 02:09

arshajii


1 Answers

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.

like image 84
Antimony Avatar answered Oct 24 '22 23:10

Antimony