invokedynamic字节码不能直接通过Java的编译器来直接生成的,它只有在碰到lambda的时候,会生成。
那么我们如果来测试invokedynamic字节码呢。可以通过asm-all-4.0.jar来自己生成。其实这个命名主要是为了动态语言而生的,
一般只有在写动态语言的解释器来需要用到。
可以看看下面一个例子,来了解一下invokedynamic是如何生成的使用的。
首先写一个生成invokedynamic的抽象类:
package invokedynamic;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@SuppressWarnings("unused")
public abstract class AbstractDynamicInvokerGenerator implements Opcodes {
public byte[] dump(String dynamicInvokerClassName, String dynamicLinkageClassName, String bootstrapMethodName, String targetMethodDescriptor)
throws Exception {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, dynamicInvokerClassName, null, "java/lang/Object", null);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
mv.visitCode();
MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
MethodType.class);
Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, dynamicLinkageClassName, bootstrapMethodName,
mt.toMethodDescriptorString());
int maxStackSize = addMethodParameters(mv);
mv.visitInvokeDynamicInsn("runCalculation", targetMethodDescriptor, bootstrap); //注意这里
mv.visitInsn(RETURN);
mv.visitMaxs(maxStackSize, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
protected abstract int addMethodParameters(MethodVisitor mv);
}
asm通过
visitInvokeDynamicInsn
命令来生成invokedynamic指令。
然后再实现一个通过invokedynamic来调用SimpleDynamicLinkageExample类bootstrapDynamic方法;
package invokedynamic.generator;
import invokedynamic.AbstractDynamicInvokerGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objectweb.asm.MethodVisitor;
public class SimpleDynamicInvokerGenerator extends
AbstractDynamicInvokerGenerator {
@Override
protected int addMethodParameters(MethodVisitor mv) {
return 0;
}
public static void main(String[] args) throws IOException, Exception {
String dynamicInvokerClassName = "invokedynamic/generator/SimpleDynamicInvoker";
FileOutputStream fos = new FileOutputStream(new File("bin/"
+ dynamicInvokerClassName + ".class"));
fos.write(new SimpleDynamicInvokerGenerator().dump(
dynamicInvokerClassName,
"invokedynamic/linkageclasses/SimpleDynamicLinkageExample",
"bootstrapDynamic", "()V"));
}
}
然后实现SimpleDynamicLinkageExample类:
package invokedynamic.linkageclasses;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@SuppressWarnings({ "unused", "rawtypes" })
public class SimpleDynamicLinkageExample {
private static MethodHandle sayHello;
private static void sayHello() {
System.out.println("There we go!");
}
public static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class thisClass = lookup.lookupClass(); // (who am I?)
sayHello = lookup.findStatic(thisClass, "sayHello", MethodType.methodType(void.class));
return new ConstantCallSite(sayHello.asType(type));
}
}
这是需要的类就写完了,需要注意的是,你需要先在Eclipse中运行SimpleDynamicInvokerGenerator,让它生成invokedynamic/generator/SimpleDynamicInvoker.class文件;
这个类是我们的入口类,还需要注意的是,我是在Eclipse中编译这些文件的,如果你的bin目录指定到别的地方,你需要修改SimpleDynamicInvokerGenerator类中的路径;
因为生成的SimpleDynamicInvoker类对Eclipse不可见,所以我们需要在命令行中来执行它;
先cd到你的工程目录下,执行
C:\Users\tmp\workspace_java\SimpleDyn\bin>java -classpath
“C:\Users\tmp\workspace_java\SimpleDyn\bin;”
invokedynamic.generator.SimpleDynamicInvoker
There we go!
那么只是怎么完成的呢:
看看SimpleDynamicInvokerGenerator的内容就知道了:
// (version 1.7 : 51.0, super bit)
public class invokedynamic.generator.SimpleDynamicInvoker {
// Method descriptor #6 ()V
// Stack: 1, Locals: 1
public SimpleDynamicInvoker();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8]
4 return
// Method descriptor #10 ([Ljava/lang/String;)V
// Stack: 0, Locals: 1
public static void main(java.lang.String[] arg0);
0 invokedynamic 0 runCalculation() : void [20]
5 return
Bootstrap methods:
0 : # 17 invokestatic invokedynamic/linkageclasses/SimpleDynamicLinkageExample.bootstrapDynamic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
}
在class中JVM通过invokedynamic来调用runCalculation方法,这个方法是我们通过asm来写入的,可以是任意的名字;
然后我们用通过Bootstrap方法将runCalculation和
SimpleDynamicLinkageExample
的
bootstrapDynamic
方法关联起来;
在
SimpleDynamicLinkageExample
中我们有通过MethodHandle找到其中的sayHello方法,然后调用它,这样就完成了invokedynamic命令和实际的函数关联了起来。
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-
example.html